diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common')
267 files changed, 35013 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/COPYING.MIT b/meta-openbmc-mods/meta-common/COPYING.MIT new file mode 100644 index 000000000..89de35479 --- /dev/null +++ b/meta-openbmc-mods/meta-common/COPYING.MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/meta-openbmc-mods/meta-common/README b/meta-openbmc-mods/meta-common/README new file mode 100644 index 000000000..9d4a8a1e6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/README @@ -0,0 +1,64 @@ +This README file contains information on the contents of the +common layer. + +Please see the corresponding sections below for details. + + +Dependencies +============ + +This layer depends on: + + URI: git://git.openembedded.org/bitbake + branch: master + + URI: git://git.openembedded.org/openembedded-core + layers: meta + branch: master + + URI: git://git.yoctoproject.org/xxxx + layers: xxxx + branch: master + + +Patches +======= + +Please submit any patches against the common layer to the +xxxx mailing list (xxxx@zzzz.org) and cc: the maintainer: + +Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com> + + +Table of Contents +================= + + I. Adding the common layer to your build + II. Misc + + +I. Adding the common layer to your build +================================================= + +--- replace with specific instructions for the common layer --- + +In order to use this layer, you need to make the build system aware of +it. + +Assuming the common layer exists at the top-level of your +yocto build tree, you can add it to the build system by adding the +location of the common layer to bblayers.conf, along with any +other layers needed. e.g.: + + BBLAYERS ?= " \ + /path/to/yocto/meta \ + /path/to/yocto/meta-poky \ + /path/to/yocto/meta-yocto-bsp \ + /path/to/yocto/meta-common \ + " + + +II. Misc +======== + +--- replace with specific information about the common layer --- diff --git a/meta-openbmc-mods/meta-common/classes/image_types_phosphor_auto.bbclass b/meta-openbmc-mods/meta-common/classes/image_types_phosphor_auto.bbclass new file mode 100644 index 000000000..91e699979 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/image_types_phosphor_auto.bbclass @@ -0,0 +1,75 @@ +# Base image class extension, inlined into every image. + +# Phosphor image types +# +# New image types based on DTS partition information +# +inherit logging + +# Image composition +FLASH_FULL_IMAGE ?= "fitImage-rootfs-${MACHINE}.bin" + +IMAGE_BASETYPE ?= "squashfs-xz" +OVERLAY_BASETYPE ?= "jffs2" + +IMAGE_TYPES += "mtd-auto" + +IMAGE_TYPEDEP_mtd-auto = "${IMAGE_BASETYPE}" +IMAGE_TYPES_MASKED += "mtd-auto" + +# Flash characteristics in KB unless otherwise noted +python() { + # TODO: find partition list in DTS + DTB_FULL_FIT_IMAGE_OFFSETS = [0x80000, 0x2480000] + d.setVar('FLASH_UBOOT_OFFSET', str(0)) + if d.getVar('IMAGE_TYPE', True) == 'pfr': + d.setVar('FLASH_SIZE', str(128*1024)) + else: + d.setVar('FLASH_SIZE', str(64*1024)) + d.setVar('FLASH_RUNTIME_OFFSETS', ' '.join( + [str(int(x/1024)) for x in DTB_FULL_FIT_IMAGE_OFFSETS] + ) + ) +} + +mk_nor_image() { + image_dst="$1" + image_size_kb=$2 + dd if=/dev/zero bs=1k count=$image_size_kb \ + | tr '\000' '\377' > $image_dst +} + +do_generate_auto() { + bbdebug 1 "do_generate_auto IMAGE_TYPE=${IMAGE_TYPE} size=${FLASH_SIZE}KB (${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.auto.mtd)" + # Assemble the flash image + mk_nor_image ${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd ${FLASH_SIZE} + dd bs=1k conv=notrunc seek=${FLASH_UBOOT_OFFSET} \ + if=${DEPLOY_DIR_IMAGE}/u-boot.${UBOOT_SUFFIX} \ + of=${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd + + for OFFSET in ${FLASH_RUNTIME_OFFSETS}; do + dd bs=1k conv=notrunc seek=${OFFSET} \ + if=${DEPLOY_DIR_IMAGE}/${FLASH_FULL_IMAGE} \ + of=${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd + done + + ln ${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd \ + ${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.auto.mtd + ln -sf ${IMAGE_NAME}.auto.mtd ${DEPLOY_DIR_IMAGE}/image-mtd +} +do_generate_auto[dirs] = "${S}/auto" +do_generate_auto[depends] += " \ + ${PN}:do_image_${@d.getVar('IMAGE_BASETYPE', True).replace('-', '_')} \ + virtual/kernel:do_deploy \ + u-boot:do_populate_sysroot \ + " + +python() { + types = d.getVar('IMAGE_FSTYPES', True).split() + + if 'mtd-auto' in types: + bb.build.addtask(# task, depends_on_task, task_depends_on, d ) + 'do_generate_auto', + 'do_build', + 'do_image_complete', d) +} diff --git a/meta-openbmc-mods/meta-common/classes/obmc-phosphor-full-fitimage.bbclass b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-full-fitimage.bbclass new file mode 100644 index 000000000..fefa65569 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-full-fitimage.bbclass @@ -0,0 +1,555 @@ +inherit uboot-sign logging + +DEPENDS += "u-boot-mkimage-native" + +SIGNING_KEY ?= "${STAGING_DIR_NATIVE}${datadir}/OpenBMC.priv" +INSECURE_KEY = "${@'${SIGNING_KEY}' == '${STAGING_DIR_NATIVE}${datadir}/OpenBMC.priv'}" +SIGNING_KEY_DEPENDS = "${@oe.utils.conditional('INSECURE_KEY', 'True', 'phosphor-insecure-signing-key-native:do_populate_sysroot', '', d)}" + +DEPS = " ${PN}:do_image_${@d.getVar('IMAGE_BASETYPE', True).replace('-', '_')} \ + virtual/kernel:do_deploy \ + u-boot:do_populate_sysroot " + +python() { + if d.getVar('IMAGE_TYPE', True) == 'pfr': + d.appendVar('DEPS', ' openssl-native:do_populate_sysroot \ + ${SIGNING_KEY_DEPENDS} \ + ${PN}:do_copy_signing_pubkey') +} + + +# Options for the device tree compiler passed to mkimage '-D' feature: +UBOOT_MKIMAGE_DTCOPTS ??= "" + +# +# Emit the fitImage ITS header +# +# $1 ... .its filename +fitimage_emit_fit_header() { + cat << EOF >> ${1} +/dts-v1/; + +/ { + description = "U-Boot fitImage for ${DISTRO_NAME}/${PV}/${MACHINE}"; + #address-cells = <1>; +EOF +} + +# +# Emit the fitImage section bits +# +# $1 ... .its filename +# $2 ... Section bit type: imagestart - image section start +# confstart - configuration section start +# sectend - section end +# fitend - fitimage end +# +fitimage_emit_section_maint() { + case $2 in + imagestart) + cat << EOF >> ${1} + + images { +EOF + ;; + confstart) + cat << EOF >> ${1} + + configurations { +EOF + ;; + sectend) + cat << EOF >> ${1} + }; +EOF + ;; + fitend) + cat << EOF >> ${1} +}; +EOF + ;; + esac +} + +# +# Emit the fitImage ITS kernel section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to kernel image +# $4 ... Compression type +# $5 ... Hash type +fitimage_emit_section_kernel() { + + kernel_csum="${5}" + + if [ -n "${kernel_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${kernel_csum}"; + }; +EOF + ) + fi + + ENTRYPOINT=${UBOOT_ENTRYPOINT} + if [ -n "${UBOOT_ENTRYSYMBOL}" ]; then + ENTRYPOINT=`${HOST_PREFIX}nm vmlinux | \ + awk '$3=="${UBOOT_ENTRYSYMBOL}" {print "0x"$1;exit}'` + fi + + cat << EOF >> ${1} + kernel@${2} { + description = "Linux kernel"; + data = /incbin/("${3}"); + type = "kernel"; + arch = "${UBOOT_ARCH}"; + os = "linux"; + compression = "${4}"; + load = <${UBOOT_LOADADDRESS}>; + entry = <${ENTRYPOINT}>; + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS DTB section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to DTB image +# $4 ... Hash type +fitimage_emit_section_dtb() { + + dtb_csum="${4}" + if [ -n "${dtb_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${dtb_csum}"; + }; +EOF + ) + fi + + cat << EOF >> ${1} + fdt@${2} { + description = "Flattened Device Tree blob"; + data = /incbin/("${3}"); + type = "flat_dt"; + arch = "${UBOOT_ARCH}"; + compression = "none"; + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS setup section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to setup image +# $4 ... Hash type +fitimage_emit_section_setup() { + + setup_csum="${4}" + if [ -n "${setup_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${setup_csum}"; + }; +EOF + ) + fi + + cat << EOF >> ${1} + setup@${2} { + description = "Linux setup.bin"; + data = /incbin/("${3}"); + type = "x86_setup"; + arch = "${UBOOT_ARCH}"; + os = "linux"; + compression = "none"; + load = <0x00090000>; + entry = <0x00090000>; + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS ramdisk section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to ramdisk image +# $4 ... Hash type +fitimage_emit_section_ramdisk() { + + ramdisk_csum="${4}" + if [ -n "${ramdisk_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${ramdisk_csum}"; + }; +EOF + ) + fi + ramdisk_ctype="none" + ramdisk_loadline="" + ramdisk_entryline="" + + if [ -n "${UBOOT_RD_LOADADDRESS}" ]; then + ramdisk_loadline="load = <${UBOOT_RD_LOADADDRESS}>;" + fi + if [ -n "${UBOOT_RD_ENTRYPOINT}" ]; then + ramdisk_entryline="entry = <${UBOOT_RD_ENTRYPOINT}>;" + fi + + case $3 in + *.gz) + ramdisk_ctype="gzip" + ;; + *.bz2) + ramdisk_ctype="bzip2" + ;; + *.lzma) + ramdisk_ctype="lzma" + ;; + *.lzo) + ramdisk_ctype="lzo" + ;; + *.lz4) + ramdisk_ctype="lz4" + ;; + esac + + cat << EOF >> ${1} + ramdisk@${2} { + description = "${INITRAMFS_IMAGE}"; + data = /incbin/("${3}"); + type = "ramdisk"; + arch = "${UBOOT_ARCH}"; + os = "linux"; + compression = "${ramdisk_ctype}"; + ${ramdisk_loadline} + ${ramdisk_entryline} + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS configuration section +# +# $1 ... .its filename +# $2 ... Linux kernel ID +# $3 ... DTB image name +# $4 ... ramdisk ID +# $5 ... config ID +# $6 ... default flag +# $7 ... Hash type +fitimage_emit_section_config() { + + conf_csum="${7}" + if [ -n "${conf_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${conf_csum}"; + }; +EOF + ) + fi + if [ -n "${UBOOT_SIGN_ENABLE}" ] ; then + conf_sign_keyname="${UBOOT_SIGN_KEYNAME}" + fi + + # Test if we have any DTBs at all + conf_desc="Linux kernel" + kernel_line="kernel = \"kernel@${2}\";" + fdt_line="" + ramdisk_line="" + setup_line="" + default_line="" + + if [ -n "${3}" ]; then + conf_desc="${conf_desc}, FDT blob" + fdt_line="fdt = \"fdt@${3}\";" + fi + + if [ -n "${4}" ]; then + conf_desc="${conf_desc}, ramdisk" + ramdisk_line="ramdisk = \"ramdisk@${4}\";" + fi + + if [ -n "${5}" ]; then + conf_desc="${conf_desc}, setup" + setup_line="setup = \"setup@${5}\";" + fi + + if [ "${6}" = "1" ]; then + default_line="default = \"conf@${3}\";" + fi + + cat << EOF >> ${1} + ${default_line} + conf@${3} { + description = "${6} ${conf_desc}"; + ${kernel_line} + ${fdt_line} + ${ramdisk_line} + ${setup_line} + ${hash_blk} +EOF + + if [ ! -z "${conf_sign_keyname}" ] ; then + + sign_line="sign-images = \"kernel\"" + + if [ -n "${3}" ]; then + sign_line="${sign_line}, \"fdt\"" + fi + + if [ -n "${4}" ]; then + sign_line="${sign_line}, \"ramdisk\"" + fi + + if [ -n "${5}" ]; then + sign_line="${sign_line}, \"setup\"" + fi + + sign_line="${sign_line};" + + cat << EOF >> ${1} + signature@1 { + algo = "${conf_csum},rsa2048"; + key-name-hint = "${conf_sign_keyname}"; + ${sign_line} + }; +EOF + fi + + cat << EOF >> ${1} + }; +EOF +} + +# +# Assemble fitImage +# +# $1 ... .its filename +# $2 ... fitImage name +# $3 ... include rootfs +fitimage_assemble() { + kernelcount=1 + dtbcount="" + DTBS="" + ramdiskcount=${3} + setupcount="" + #hash_type="sha256" + hash_type="" + rm -f ${1} ${2} + + # + # Step 0: find the kernel image in the deploy/images/$machine dir + # + KIMG="" + for KTYPE in zImage bzImage vmlinuz; do + if [ -e "${DEPLOY_DIR_IMAGE}/${ktype}" ]; then + KIMG="${DEPLOY_DIR_IMAGE}/${KTYPE}" + break + fi + done + if [ -z "${KIMG}" ]; then + bbdebug 1 "Failed to find kernel image to pack into full fitimage" + return 1 + fi + + fitimage_emit_fit_header ${1} + + # + # Step 1: Prepare a kernel image section. + # + fitimage_emit_section_maint ${1} imagestart + + fitimage_emit_section_kernel ${1} "${kernelcount}" "${KIMG}" "none" "${hash_type}" + + # + # Step 2: Prepare a DTB image section + # + if [ -n "${KERNEL_DEVICETREE}" ]; then + dtbcount=1 + for DTB in ${KERNEL_DEVICETREE}; do + if echo ${DTB} | grep -q '/dts/'; then + bberror "${DTB} contains the full path to the the dts file, but only the dtb name should be used." + DTB=`basename ${DTB} | sed 's,\.dts$,.dtb,g'` + fi + DTB_PATH="${DEPLOY_DIR_IMAGE}/${DTB}" + if [ ! -e "${DTB_PATH}" ]; then + bbwarn "${DTB_PATH} does not exist" + continue + fi + + DTB=$(echo "${DTB}" | tr '/' '_') + DTBS="${DTBS} ${DTB}" + fitimage_emit_section_dtb ${1} ${DTB} ${DTB_PATH} "${hash_type}" + done + fi + + # + # Step 3: Prepare a setup section. (For x86) + # + if [ -e arch/${ARCH}/boot/setup.bin ]; then + setupcount=1 + fitimage_emit_section_setup ${1} "${setupcount}" arch/${ARCH}/boot/setup.bin "${hash_type}" + fi + + # + # Step 4: Prepare a ramdisk section. + # + if [ "x${ramdiskcount}" = "x1" ] ; then + bbdebug 1 "searching for requested rootfs" + # Find and use the first initramfs image archive type we find + for img in squashfs-lz4 squashfs-xz squashfs cpio.lz4 cpio.lzo cpio.lzma cpio.xz cpio.gz cpio; do + initramfs_path="${DEPLOY_DIR_IMAGE}/${IMAGE_BASENAME}-${MACHINE}.${img}" + bbdebug 1 "looking for ${initramfs_path}" + if [ -e "${initramfs_path}" ]; then + bbdebug 1 "Found ${initramfs_path}" + fitimage_emit_section_ramdisk ${1} "${ramdiskcount}" "${initramfs_path}" "${hash_type}" + break + fi + done + fi + + fitimage_emit_section_maint ${1} sectend + + # Force the first Kernel and DTB in the default config + kernelcount=1 + if [ -n "${dtbcount}" ]; then + dtbcount=1 + fi + + # + # Step 5: Prepare a configurations section + # + fitimage_emit_section_maint ${1} confstart + + if [ -n "${DTBS}" ]; then + i=1 + for DTB in ${DTBS}; do + fitimage_emit_section_config ${1} "${kernelcount}" "${DTB}" "${ramdiskcount}" "${setupcount}" "`expr ${i} = ${dtbcount}`" "${hash_type}" + i=`expr ${i} + 1` + done + fi + + fitimage_emit_section_maint ${1} sectend + + fitimage_emit_section_maint ${1} fitend + + # + # Step 6: Assemble the image + # + uboot-mkimage \ + ${@'-D "${UBOOT_MKIMAGE_DTCOPTS}"' if len('${UBOOT_MKIMAGE_DTCOPTS}') else ''} \ + -f ${1} ${2} + + # + # Step 7: Sign the image and add public key to U-Boot dtb + # + if [ "x${UBOOT_SIGN_ENABLE}" = "x1" ] ; then + uboot-mkimage \ + ${@'-D "${UBOOT_MKIMAGE_DTCOPTS}"' if len('${UBOOT_MKIMAGE_DTCOPTS}') else ''} \ + -F -k "${UBOOT_SIGN_KEYDIR}" \ + -K "${DEPLOY_DIR_IMAGE}/${UBOOT_DTB_BINARY}" \ + -r ${2} + fi +} + +python do_generate_phosphor_manifest() { + import os.path + b = d.getVar('B', True) + manifest_filename = os.path.join(b, "MANIFEST") + version = do_get_version(d) + with open(manifest_filename, 'w') as fd: + fd.write('purpose=xyz.openbmc_project.Software.Version.VersionPurpose.BMC\n') + fd.write('version={}\n'.format(version.strip('"'))) + fd.write('KeyType={}\n'.format("OpenBMC")) + fd.write('HashType=RSA-SHA256\n') +} + +make_signatures() { + signature_files="" + for file in "$@"; do + openssl dgst -sha256 -sign ${SIGNING_KEY} -out "${file}.sig" $file + signature_files="${signature_files} ${file}.sig" + done +} + +def get_pubkey_type(d): + return os.listdir(get_pubkey_basedir(d))[0] + +def get_pubkey_path(d): + return os.path.join( + get_pubkey_basedir(d), + get_pubkey_type(d), + 'publickey') +python do_copy_signing_pubkey() { + with open(get_pubkey_path(d), 'r') as read_fd: + with open('publickey', 'w') as write_fd: + write_fd.write(read_fd.read()) +} + +do_copy_signing_pubkey[dirs] = "${S}" +do_copy_signing_pubkey[depends] += " \ + phosphor-image-signing:do_populate_sysroot \ + " + +do_image_fitimage_rootfs() { + bbdebug 1 "check for rootfs phosphor fitimage" + cd ${B} + bbdebug 1 "building rootfs phosphor fitimage" + fitimage_assemble fitImage-rootfs-${MACHINE}-${DATETIME}.its \ + fitImage-rootfs-${MACHINE}-${DATETIME}.bin 1 + + for SFX in its bin; do + SRC="fitImage-rootfs-${MACHINE}-${DATETIME}.${SFX}" + SYM="fitImage-rootfs-${MACHINE}.${SFX}" + if [ -e "${B}/${SRC}" ]; then + install -m 0644 "${B}/${SRC}" "${DEPLOY_DIR_IMAGE}/${SRC}" + ln -sf "${SRC}" "${DEPLOY_DIR_IMAGE}/${SYM}" + fi + done + ln -sf "${DEPLOY_DIR_IMAGE}/fitImage-rootfs-${MACHINE}.bin" "image-runtime" + # build a tarball with the right parts: MANIFEST, signatures, etc. + # create a directory for the tarball + mkdir -p "${B}/img" + cd "${B}/img" + # add symlinks for the contents + ln -sf "${DEPLOY_DIR_IMAGE}/u-boot.${UBOOT_SUFFIX}" "image-u-boot" + ln -sf "${DEPLOY_DIR_IMAGE}/fitImage-rootfs-${MACHINE}.bin" "image-runtime" + # add the manifest + bbdebug 1 "Manifest file: ${B}/MANIFEST" + ln -sf ${B}/MANIFEST . + # touch the required files to minimize change + touch image-kernel image-rofs image-rwfs + + if [ "${IMAGE_TYPE}" = "pfr" ]; then + ln -sf ${S}/publickey publickey + make_signatures image-u-boot image-kernel image-rofs image-rwfs image-runtime MANIFEST publickey + # tar up the update package + tar -h -cvf "${DEPLOY_DIR_IMAGE}/${PN}-image-update-${MACHINE}-${DATETIME}.tar" image-u-boot image-runtime image-kernel image-rofs image-rwfs MANIFEST publickey ${signature_files} + else + tar -h -cvf "${DEPLOY_DIR_IMAGE}/${PN}-image-update-${MACHINE}-${DATETIME}.tar" MANIFEST image-u-boot image-runtime image-kernel image-rofs image-rwfs + fi + # make a symlink + ln -sf "${PN}-image-update-${MACHINE}-${DATETIME}.tar" "${DEPLOY_DIR_IMAGE}/image-update-${MACHINE}" + ln -sf "image-update-${MACHINE}" "${DEPLOY_DIR_IMAGE}/image-update" +} + +do_image_fitimage_rootfs[vardepsexclude] = "DATETIME" +do_image_fitimage_rootfs[depends] += " ${DEPS}" + + +addtask do_image_fitimage_rootfs before do_generate_auto after do_image_complete +addtask do_generate_phosphor_manifest before do_image_fitimage_rootfs after do_image_complete diff --git a/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-common.bbclass b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-common.bbclass new file mode 100644 index 000000000..f10b2389f --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-common.bbclass @@ -0,0 +1,38 @@ +inherit obmc-phosphor-image + +IMAGE_INSTALL_append = " \ + fan-default-speed \ + bmcweb \ + dbus-broker \ + dtc \ + dtoverlay \ + entity-manager \ + ipmitool \ + ipmi-providers \ + intel-ipmi-oem \ + phosphor-ipmi-ipmb \ + phosphor-node-manager-proxy \ + dbus-sensors \ + phosphor-webui \ + rest-dbus-static \ + phosphor-pid-control \ + phosphor-host-postd \ + smbios-mdrv1 \ + phosphor-certificate-manager \ + set-passthrough \ + phosphor-sel-logger \ + gpiodaemon \ + smbios-mdrv2 \ + obmc-ikvm \ + system-watchdog \ + frb2-watchdog \ + srvcfg-manager \ + callback-manager \ + post-code-manager \ + preinit-mounts \ + mtd-utils-ubifs \ + " + +# this package was flagged as a security risk +IMAGE_INSTALL_remove += " lrzsz" + diff --git a/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-dev.bbclass b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-dev.bbclass new file mode 100644 index 000000000..6a1ac3f14 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-dev.bbclass @@ -0,0 +1,7 @@ +IMAGE_INSTALL_append = " \ + mtd-util \ + io-app \ + intel-fw-update \ + lpc-cmds \ + beeper-test \ + " diff --git a/meta-openbmc-mods/meta-common/conf/layer.conf b/meta-openbmc-mods/meta-common/conf/layer.conf new file mode 100644 index 000000000..7e61bfd50 --- /dev/null +++ b/meta-openbmc-mods/meta-common/conf/layer.conf @@ -0,0 +1,11 @@ +# We have a conf and classes directory, add to BBPATH +BBPATH .= ":${LAYERDIR}" + +# We have recipes-* directories, add to BBFILES +BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ + ${LAYERDIR}/recipes-*/*/*.bbappend" + +BBFILE_COLLECTIONS += "common" +BBFILE_PATTERN_common = "^${LAYERDIR}/" +BBFILE_PRIORITY_common = "10" +LAYERSERIES_COMPAT_common = "thud" diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0001-flash-use-readX-writeX-not-udelay.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0001-flash-use-readX-writeX-not-udelay.patch new file mode 100644 index 000000000..73ab78a65 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0001-flash-use-readX-writeX-not-udelay.patch @@ -0,0 +1,424 @@ +Index: u-boot/arch/arm/mach-aspeed/flash.c +=================================================================== +--- u-boot.orig/arch/arm/mach-aspeed/flash.c ++++ u-boot/arch/arm/mach-aspeed/flash.c +@@ -28,6 +28,7 @@ + #include <common.h> + #include <asm/processor.h> + #include <asm/byteorder.h> ++#include <asm/io.h> + #include <environment.h> + + #include <asm/arch/ast_scu.h> +@@ -199,7 +200,7 @@ static void reset_flash (flash_info_t * + if (info->dualport) + ulCtrlData |= 0x08; + #endif +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } + +@@ -228,28 +229,22 @@ static void enable_write (flash_info_t * + + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x06); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x06, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x02)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } + +@@ -280,30 +275,23 @@ static void write_status_register (flash + + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x01); +- udelay(10); +- *(uchar *) (base) = (uchar) (data); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x01, base); ++ writeb(data, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (jReg & 0x01); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + } + + static void enable4b (flash_info_t * info) +@@ -330,13 +318,11 @@ static void enable4b (flash_info_t * inf + + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0xb7); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0xb7, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } /* enable4b */ + +@@ -366,29 +352,23 @@ static void enable4b_spansion (flash_inf + /* Enable 4B: BAR0 D[7] = 1 */ + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x17); +- udelay(10); +- *(uchar *) (base) = (uchar) (0x80); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x17, base); ++ writeb(0x80, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x16); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x16, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x80)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } /* enable4b_spansion */ + +@@ -420,14 +400,11 @@ static void enable4b_numonyx (flash_info + /* Enable 4B: CMD:0xB7 */ + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0xB7); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0xB7, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } /* enable4b_numonyx */ + +@@ -463,63 +440,49 @@ static void flash_write_buffer (flash_in + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x02); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x02, base); + if (info->address32) + { +- *(uchar *) (base) = (uchar) ((offset & 0xff000000) >> 24); +- udelay(10); ++ writeb((uchar) ((offset & 0xff000000) >> 24), base); + } +- *(uchar *) (base) = (uchar) ((offset & 0xff0000) >> 16); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x00ff00) >> 8); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x0000ff)); +- udelay(10); ++ writeb((uchar) ((offset & 0xff0000) >> 16), base); ++ writeb((uchar) ((offset & 0x00ff00) >> 8), base); ++ writeb((uchar) ((offset & 0x0000ff)), base); + + for (j=0; j<len; j++) + { +- *(uchar *) (base) = *(uchar *) (src++); +- udelay(10); ++ writeb(*src++, base); + } + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while ((jReg & 0x01)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + /* RFSR */ + if (info->specificspi == SpecificSPI_N25Q512) + { + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x70); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x70, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x80)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + } + } + +@@ -603,57 +566,44 @@ int flash_erase (flash_info_t * info, in + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0xd8); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0xd8, base); + if (info->address32) + { +- *(uchar *) (base) = (uchar) ((offset & 0xff000000) >> 24); +- udelay(10); ++ writeb((uchar) ((offset & 0xff000000) >> 24), base); + } +- *(uchar *) (base) = (uchar) ((offset & 0xff0000) >> 16); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x00ff00) >> 8); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x0000ff)); +- udelay(10); ++ writeb((uchar) ((offset & 0xff0000) >> 16), base); ++ writeb((uchar) ((offset & 0x00ff00) >> 8), base); ++ writeb((uchar) ((offset & 0x0000ff)), base); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while ((jReg & 0x01)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + /* RFSR */ + if (info->specificspi == SpecificSPI_N25Q512) + { + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x70); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x70, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x80)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + } + + putc ('.'); +@@ -764,22 +714,16 @@ static ulong flash_get_size (ulong base, + } + + /* Get Flash ID */ +- ulCtrlData = *(ulong *) (info->reg_base + CtrlOffset) & CMD_MASK; ++ ulCtrlData = readl(info->reg_base + CtrlOffset) & CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (vbase) = (uchar) (0x9F); +- udelay(10); +- ch[0] = *(volatile uchar *)(vbase); +- udelay(10); +- ch[1] = *(volatile uchar *)(vbase); +- udelay(10); +- ch[2] = *(volatile uchar *)(vbase); +- udelay(10); +- ulCtrlData = *(ulong *) (info->reg_base + CtrlOffset) & CMD_MASK; ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x9F, vbase); ++ ch[0] = readb(vbase); ++ ch[1] = readb(vbase); ++ ch[2] = readb(vbase); ++ ulCtrlData = readl(info->reg_base + CtrlOffset) & CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + ulID = ((ulong)ch[0]) | ((ulong)ch[1] << 8) | ((ulong)ch[2] << 16) ; + info->flash_id = ulID; + +@@ -1294,13 +1238,13 @@ static ulong flash_get_size (ulong base, + + if (info->address32) { + #ifndef AST_SOC_G5 +- reg = *((volatile ulong*) 0x1e6e2070); /* set H/W Trappings */ ++ reg = readl(0x1e6e2070); /* set H/W Trappings */ + reg |= 0x10; +- *((volatile ulong*) 0x1e6e2070) = reg; ++ writel(reg, 0x1e6e2070); + #endif +- reg = *((volatile ulong*) (info->reg_base + 0x4)); /* enable 32b control bit*/ ++ reg = readl(info->reg_base + 0x4); /* enable 32b control bit*/ + reg |= (0x01 << info->CE); +- *((volatile ulong*) (info->reg_base + 0x4)) = reg; ++ writel(reg, info->reg_base + 0x4); + + /* set flash chips to 32bits addressing mode */ + if ((info->flash_id & 0xFF) == 0x01) /* Spansion */ +@@ -1322,7 +1266,7 @@ unsigned long flash_init (void) + unsigned long size = 0; + int i; + +- *((volatile ulong*) AST_FMC_BASE) |= 0x800f0000; /* enable Flash Write */ ++ writel(readl(AST_FMC_BASE) | 0x800f0000, AST_FMC_BASE); /* enable Flash Write */ + + /* Init: FMC */ + /* BANK 0 : FMC CS0 , 1: FMC CS1, */ +@@ -1352,7 +1296,7 @@ unsigned long flash_init (void) + #ifdef CONFIG_SPI0_CS + //pin switch by trap[13:12] -- [0:1] Enable SPI Master + ast_scu_spi_master(1); /* enable SPI master */ +- *((volatile ulong*) AST_FMC_SPI0_BASE) |= 0x10000; /* enable Flash Write */ ++ writel(readl(AST_FMC_SPI0_BASE) | 0x10000, AST_FMC_SPI0_BASE); /* enable Flash Write */ + flash_info[CONFIG_FMC_CS].sysspi = 1; + flash_info[CONFIG_FMC_CS].reg_base = AST_FMC_SPI0_BASE; + flash_info[CONFIG_FMC_CS].flash_id = FLASH_UNKNOWN; +@@ -1403,21 +1347,24 @@ void memmove_dma(void * dest,const void + poll_time = 100; /* set 100 us as default */ + + /* force end of burst read */ +- *(volatile ulong *) (AST_FMC_BASE + CS0_CTRL) |= CE_HIGH; +- *(volatile ulong *) (AST_FMC_BASE + CS0_CTRL) &= ~CE_HIGH; +- +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_CONTROL) = (ulong) (~FLASH_DMA_ENABLE); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_FLASH_BASE) = (ulong) (src); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_DRAM_BASE) = (ulong) (dest); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_LENGTH) = (ulong) (count_align); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_CONTROL) = (ulong) (FLASH_DMA_ENABLE); ++ data = readl(AST_FMC_BASE + CS0_CTRL); ++ writel(data | CE_HIGH, AST_FMC_BASE + CS0_CTRL); ++ writel(data & ~CE_HIGH, AST_FMC_BASE + CS0_CTRL); ++ ++ writel(~FLASH_DMA_ENABLE, AST_FMC_BASE + REG_FLASH_DMA_CONTROL); ++ writel((ulong)src, AST_FMC_BASE + REG_FLASH_DMA_FLASH_BASE); ++ writel((ulong)dest, AST_FMC_BASE + REG_FLASH_DMA_DRAM_BASE); ++ writel(count_align, AST_FMC_BASE + REG_FLASH_DMA_LENGTH); ++ writel(FLASH_DMA_ENABLE, AST_FMC_BASE + REG_FLASH_DMA_CONTROL); + + /* wait poll */ + do { + udelay(poll_time); +- data = *(ulong *) (AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); ++ data = readl(AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); + } while (!(data & FLASH_STATUS_DMA_READY)); + + /* clear status */ +- *(ulong *) (AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS) |= FLASH_STATUS_DMA_CLEAR; ++ data = readl(AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); ++ writel(data | FLASH_STATUS_DMA_CLEAR, ++ AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); + } diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0002-intel-layout-environment-addr.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0002-intel-layout-environment-addr.patch new file mode 100644 index 000000000..86fa5b7d5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0002-intel-layout-environment-addr.patch @@ -0,0 +1,42 @@ +Index: u-boot/include/configs/ast-common.h +=================================================================== +--- u-boot.orig/include/configs/ast-common.h ++++ u-boot/include/configs/ast-common.h +@@ -103,10 +103,13 @@ + #define CONFIG_SYS_MAX_FLASH_BANKS (CONFIG_FMC_CS) + #define CONFIG_SYS_MAX_FLASH_SECT (8192) /* max number of sectors on one chip */ + #define CONFIG_ENV_IS_IN_FLASH 1 +-#define CONFIG_ENV_ADDR (AST_FMC_CS0_BASE + 0x60000) ++#define CONFIG_ENV_OFFSET 0x2400000 /* environment starts here */ ++#define CONFIG_ENV_ADDR (AST_FMC_CS0_BASE + CONFIG_ENV_OFFSET) ++#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */ + +-#define CONFIG_ENV_OFFSET 0x60000 /* environment starts here */ +-#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */ ++#define CONFIG_ENV_OFFSET_REDUND (CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE) ++#define CONFIG_ENV_ADDR_REDUND (AST_FMC_CS0_BASE + CONFIG_ENV_OFFSET_REDUND) ++#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE + + #define CONFIG_BOOTCOMMAND "bootm 20080000" + #define CONFIG_ENV_OVERWRITE +Index: u-boot/common/board_r.c +=================================================================== +--- u-boot.orig/common/board_r.c ++++ u-boot/common/board_r.c +@@ -494,10 +494,14 @@ static int should_load_env(void) + static int initr_env(void) + { + /* initialize environment */ +- if (should_load_env()) ++ if (should_load_env()) { ++ /* try again, in case the environment failed to load the first time */ ++ if (!gd->env_valid) ++ env_init(); + env_relocate(); +- else ++ } else { + set_default_env(NULL); ++ } + #ifdef CONFIG_OF_CONTROL + setenv_addr("fdtcontroladdr", gd->fdt_blob); + #endif diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch new file mode 100644 index 000000000..8bc0a3ed3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch @@ -0,0 +1,75 @@ +From 2f0e14630abec2c9679d21901072648c7802f2c4 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Tue, 11 Sep 2018 16:24:06 +0800 +Subject: [PATCH] Make sure debug uart is using 24MHz clock source + +u-boot defines the uart5(debug console) as 24MHz, +set the SCU14[28] to 0, to make sure the clock source is 24M + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + arch/arm/include/asm/arch-aspeed/ast_scu.h | 2 ++ + arch/arm/include/asm/arch-aspeed/platform.h | 1 + + arch/arm/mach-aspeed/ast-scu.c | 6 ++++++ + board/aspeed/ast-g5/ast-g5.c | 8 ++++++++ + 4 files changed, 17 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h +index d248416..98e6335 100644 +--- a/arch/arm/include/asm/arch-aspeed/ast_scu.h ++++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h +@@ -45,4 +45,6 @@ extern void ast_scu_init_eth(u8 num); + extern void ast_scu_multi_func_eth(u8 num); + extern void ast_scu_multi_func_romcs(u8 num); + ++void ast_config_uart5_clk(void); ++ + #endif +diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h +index c9c7a81..a423052 100644 +--- a/arch/arm/include/asm/arch-aspeed/platform.h ++++ b/arch/arm/include/asm/arch-aspeed/platform.h +@@ -27,6 +27,7 @@ + #include <asm/arch/ast2400_platform.h> + #elif defined(AST_SOC_G5) + #include <asm/arch/ast_g5_platform.h> ++#define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_early_init_f */ + #else + #err "No define for platform.h" + #endif +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index 0cc0d67..902263b 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -496,3 +496,9 @@ void ast_scu_get_who_init_dram(void) + break; + } + } ++ ++void ast_config_uart5_clk(void) ++{ ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & ++ ~(1 << 28), AST_SCU_MISC2_CTRL); ++} +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index e67a4bf..5a1fade 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -16,6 +16,14 @@ + + DECLARE_GLOBAL_DATA_PTR; + ++int board_early_init_f(void) ++{ ++ /* make sure uart5 is using 24MHz clock */ ++ ast_config_uart5_clk(); ++ ++ return 0; ++} ++ + int board_init(void) + { + gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0005-enable-passthrough-in-uboot.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0005-enable-passthrough-in-uboot.patch new file mode 100644 index 000000000..0385a5e31 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0005-enable-passthrough-in-uboot.patch @@ -0,0 +1,56 @@ +From b344cf4462acb1f043ed903ccee713e24ce7226d Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 7 Nov 2018 13:57:57 +0800 +Subject: [PATCH 1/1] enable passthrough in uboot + +--- + arch/arm/mach-aspeed/ast-scu.c | 22 ++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5.c | 2 ++ + 2 files changed, 24 insertions(+) + +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index 902263b28b..c83931ed54 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -502,3 +502,25 @@ void ast_config_uart5_clk(void) + ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & + ~(1 << 28), AST_SCU_MISC2_CTRL); + } ++ ++ ++void ast_enable_pass_through(void) ++{ ++ //Enable GPIOE pin mode, SCU80[16:23] = 00 */ ++ ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL1) & (~0x00FF0000), ++ AST_SCU_FUN_PIN_CTRL1); ++ ++ //Enable all pass through pins by setting SCU8C[15:12] = 0x3. ++ //Pass-through pins set: ++ //GPIOE0 -> GPIOE1 ++ //GPIOE2 -> GPIOE3 ++ ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL4) | (0x3000), ++ AST_SCU_FUN_PIN_CTRL4); ++ ++ //Disable HWStrap for GPIOE pass-through mode ++ //The write operation to SCU70(0x1e6e2070) only can set to '1'. ++ //To clear to '0', it must write '1' to 0x1e6e207c ++ if (ast_scu_read(AST_SCU_HW_STRAP1) & (0x1 << 22)){ ++ ast_scu_write((0x1 << 22), AST_SCU_REVISION_ID); ++ } ++} +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index 5a1fadeedd..b492003f51 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -20,6 +20,8 @@ int board_early_init_f(void) + { + /* make sure uart5 is using 24MHz clock */ + ast_config_uart5_clk(); ++ /*enable pass through*/ ++ ast_enable_pass_through(); + + return 0; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0006-Add-Aspeed-g5-interrupt-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0006-Add-Aspeed-g5-interrupt-support.patch new file mode 100644 index 000000000..dbaf7b362 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0006-Add-Aspeed-g5-interrupt-support.patch @@ -0,0 +1,389 @@ +From f33755167ddcdebbf56bc875e4091990273c6997 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 10:21:40 -0800 +Subject: [PATCH 1/7] Add Aspeed g5 interrupt support + +This adds a few new files to the board g5 directory. Several Intel +features require interrupts running in U-Boot, so this adds basic +interrupt registration and handling support. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Id7072f1408dcf364968b1b74f2192e50a22a82f0 +--- + Kconfig | 13 ++ + arch/arm/lib/interrupts.c | 11 ++ + board/aspeed/ast-g5/Makefile | 4 +- + board/aspeed/ast-g5/ast-g5-irq.c | 176 ++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-irq.h | 39 ++++++ + board/aspeed/ast-g5/ast-g5.c | 3 + + board/aspeed/ast-g5/ast-g5.h | 7 ++ + cmd/Kconfig | 5 + + configs/ast_g5_ncsi_2boot_defconfig | 1 + + configs/ast_g5_ncsi_defconfig | 1 + + configs/ast_g5_phy_defconfig | 1 + + 11 files changed, 260 insertions(+), 1 deletion(-) + create mode 100644 board/aspeed/ast-g5/ast-g5-irq.c + create mode 100644 board/aspeed/ast-g5/ast-g5-irq.h + create mode 100644 board/aspeed/ast-g5/ast-g5.h + +diff --git a/Kconfig b/Kconfig +index 3ceff25032..d6439d01ca 100644 +--- a/Kconfig ++++ b/Kconfig +@@ -115,6 +115,19 @@ if EXPERT + When disabling this, please check if malloc calls, maybe + should be replaced by calloc - if one expects zeroed memory. + endif ++ ++config USE_IRQ ++ bool "Use interrupts" ++ default n ++ ++config STACKSIZE_IRQ ++ int "Size for IRQ stack (only if USE_IRQ enabled)" ++ default 16384 ++ ++config STACKSIZE_FIQ ++ int "Size for FIQ stack (only if USE_IRQ enabled)" ++ default 16384 ++ + endmenu # General setup + + menu "Boot images" +diff --git a/arch/arm/lib/interrupts.c b/arch/arm/lib/interrupts.c +index ed83043abb..a96b3aa070 100644 +--- a/arch/arm/lib/interrupts.c ++++ b/arch/arm/lib/interrupts.c +@@ -94,6 +94,17 @@ int disable_interrupts (void) + : "memory"); + return (old & 0x80) == 0; + } ++ ++int global_interrupts_enabled(void) ++{ ++ unsigned long old; ++ __asm__ __volatile__("mrs %0, cpsr\n" ++ : "=r" (old) ++ : ++ : "memory"); ++ return (old & 0x80) == 0; ++} ++ + #else + int interrupt_init (void) + { +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index d1d7f8525e..d41b11589f 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -1 +1,2 @@ +-obj-y = ast-g5.o ++obj-y += ast-g5.o ++obj-y += ast-g5-irq.o +diff --git a/board/aspeed/ast-g5/ast-g5-irq.c b/board/aspeed/ast-g5/ast-g5-irq.c +new file mode 100644 +index 0000000000..860f16cf05 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-irq.c +@@ -0,0 +1,176 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <netdev.h> ++ ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/ast-sdmc.h> ++#include <asm/io.h> ++ ++#include "ast-g5.h" ++#include "ast-g5-irq.h" ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++#ifdef CONFIG_USE_IRQ ++ ++#define VIC_STATUS_L 0x80 ++#define VIC_STATUS_H 0x84 ++#define VIC_IRQ_SELECTION_L 0x98 ++#define VIC_IRQ_SELECTION_H 0x9C ++#define VIC_ENABLE_L 0xA0 ++#define VIC_ENABLE_H 0xA4 ++#define VIC_ENABLE_CLEAR_L 0xA8 ++#define VIC_ENABLE_CLEAR_H 0xAC ++#define VIC_INTERRUPT_CLEAR_L 0xD8 ++#define VIC_INTERRUPT_CLEAR_H 0xDC ++ ++#define VIC_CLEAR_ALL (~0) ++ ++int arch_interrupt_init_early(void) ++{ ++ writel(VIC_CLEAR_ALL, AST_VIC_BASE + VIC_ENABLE_CLEAR_L); ++ writel(VIC_CLEAR_ALL, AST_VIC_BASE + VIC_ENABLE_CLEAR_H); ++ return 0; ++} ++int arch_interrupt_init(void) ++{ ++ return 0; ++} ++ ++#define AST_IRQ_START_L 0 ++#define AST_IRQ_END_L 31 ++#define AST_IRQ_START_H 32 ++#define AST_IRQ_END_H 63 ++#define AST_IRQ_COUNT 64 ++static interrupt_handler_t *handlers[AST_IRQ_COUNT] = {NULL}; ++static unsigned long irq_total = 0; ++static unsigned long irq_counts[AST_IRQ_COUNT] = {0}; ++ ++int request_irq(int irq, interrupt_handler_t *handler) ++{ ++ if (irq < AST_IRQ_START_L || irq > AST_IRQ_END_H) { ++ printf("irq %d out of range\n", irq); ++ return -1; ++ } ++ if (handlers[irq]) { ++ printf("irq %d already in use (%p)\n", irq, handlers[irq]); ++ return -1; ++ } ++ handlers[irq] = handler; ++ if (irq < AST_IRQ_START_H) { ++ writel((1 << irq), AST_VIC_BASE + VIC_ENABLE_L); ++ } else { ++ writel((1 << (irq - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_H); ++ } ++ return 0; ++} ++ ++int release_irq(int irq) ++{ ++ if (irq < AST_IRQ_START_L || irq > AST_IRQ_END_H) { ++ return -1; ++ } ++ if (handlers[irq]) { ++ handlers[irq] = NULL; ++ if (irq < AST_IRQ_START_H) { ++ writel((1 << irq), AST_VIC_BASE + VIC_ENABLE_CLEAR_L); ++ } else { ++ writel((1 << (irq - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_CLEAR_H); ++ } ++ } ++ return 0; ++} ++ ++extern int global_interrupts_enabled(void); ++int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int i; ++ int enabled = global_interrupts_enabled(); ++ unsigned long long irqs_enabled = ++ ((unsigned long long)readl(AST_VIC_BASE + VIC_ENABLE_H)) ++ << AST_IRQ_START_H ++ | readl(AST_VIC_BASE + VIC_ENABLE_L); ++ printf("interrupts %sabled\n", (enabled ? "en" : "dis")); ++ for (i = AST_IRQ_START_L; i < AST_IRQ_COUNT; i++) { ++ printf("% 2i (% 3s): %lu\n", i, ++ ((irqs_enabled & 1) ? "on" : "off"), irq_counts[i]); ++ irqs_enabled >>= 1; ++ } ++ printf("total: %lu\n", irq_total); ++ return 0; ++} ++ ++void do_irq(struct pt_regs *pt_regs) ++{ ++ uint32_t irq = readl(AST_VIC_BASE + VIC_STATUS_L); ++ int i; ++ irq_total++; ++ if (irq) { ++ // handler irq0-31 ++ for (i = AST_IRQ_START_L; i <= AST_IRQ_END_L; i++) { ++ if (irq & (1 << i)) { ++ irq_counts[i]++; ++ /* mask */ ++ writel((1 << i), ++ AST_VIC_BASE + VIC_ENABLE_CLEAR_L); ++ if (handlers[i]) { ++ handlers[i](pt_regs); ++ /* clear */ ++ writel((1 << i), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_L); ++ /* unmask */ ++ writel((1 << i), ++ AST_VIC_BASE + VIC_ENABLE_L); ++ } else { ++ printf("unexpected interrupt %i; masking\n", ++ i); ++ /* clear; do not unmask */ ++ writel((1 << i), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_L); ++ } ++ } ++ } ++ } ++ irq = readl(AST_VIC_BASE + VIC_STATUS_H); ++ if (irq) { ++ // handler irq32-63 ++ for (i = AST_IRQ_START_H; i <= AST_IRQ_END_H; i++) { ++ if (irq & (1 << (i - AST_IRQ_START_H))) { ++ irq_counts[i]++; ++ /* mask */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_CLEAR_H); ++ if (handlers[i]) { ++ handlers[i](pt_regs); ++ /* clear */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_H); ++ /* unmask */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_H); ++ } else { ++ printf("unexpected interrupt %i; masking\n", ++ i); ++ /* clear; do not unmask */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_H); ++ } ++ } ++ } ++ } ++} ++#endif +diff --git a/board/aspeed/ast-g5/ast-g5-irq.h b/board/aspeed/ast-g5/ast-g5-irq.h +new file mode 100644 +index 0000000000..703eeabf13 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-irq.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __AST_G5_IRQ_H__ ++#define __AST_G5_IRQ_H__ ++ ++#include <common.h> ++ ++#ifdef CONFIG_USE_IRQ ++ ++int arch_interrupt_init_early(void); ++ ++int request_irq(int irq, interrupt_handler_t *handler); ++ ++int release_irq(int irq); ++ ++#else /* CONFIG_USE_IRQ */ ++ ++int arch_interrupt_init_early(void) { ++ return 0; ++} ++ ++int request_irq(int irq, interrupt_handler_t *handler) { ++ return -1; ++} ++ ++int release_irq(int irq) { ++ return -1; ++} ++ ++#endif /* CONFIG_USE_IRQ */ ++ ++#endif /* __AST_G5_IRQ_H__ */ +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index b492003f51..2472aa3603 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -14,6 +14,8 @@ + #include <asm/arch/ast-sdmc.h> + #include <asm/io.h> + ++#include "ast-g5.h" ++ + DECLARE_GLOBAL_DATA_PTR; + + int board_early_init_f(void) +@@ -22,6 +24,7 @@ int board_early_init_f(void) + ast_config_uart5_clk(); + /*enable pass through*/ + ast_enable_pass_through(); ++ arch_interrupt_init_early(); + + return 0; + } +diff --git a/board/aspeed/ast-g5/ast-g5.h b/board/aspeed/ast-g5/ast-g5.h +new file mode 100644 +index 0000000000..9fd10eccb3 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5.h +@@ -0,0 +1,7 @@ ++#ifndef _AST_G5_H_ ++#define _AST_G5_H_ ++ ++#include <common.h> ++#include "ast-g5-irq.h" ++ ++#endif /* _AST_G5_H_ */ +diff --git a/cmd/Kconfig b/cmd/Kconfig +index d69b817c82..33be2407d2 100644 +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -313,6 +313,11 @@ endmenu + + menu "Device access commands" + ++config CMD_IRQ ++ bool "interrupts - enable/disable interrupts" ++ depends on USE_IRQ ++ default y ++ + config CMD_DM + bool "dm - Access to driver model information" + depends on DM +diff --git a/configs/ast_g5_ncsi_2boot_defconfig b/configs/ast_g5_ncsi_2boot_defconfig +index 2d28c86966..d5b7894a9e 100644 +--- a/configs/ast_g5_ncsi_2boot_defconfig ++++ b/configs/ast_g5_ncsi_2boot_defconfig +@@ -33,3 +33,4 @@ CONFIG_CMD_CRC32=y + CONFIG_LOOPW=y + CONFIG_CMD_MEMTEST=y + CONFIG_CMD_MX_CYCLIC=y ++CONFIG_USE_IRQ=y +diff --git a/configs/ast_g5_ncsi_defconfig b/configs/ast_g5_ncsi_defconfig +index 74029ed514..9481e5fb6e 100644 +--- a/configs/ast_g5_ncsi_defconfig ++++ b/configs/ast_g5_ncsi_defconfig +@@ -11,3 +11,4 @@ CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y + CONFIG_SYS_NS16550=y ++CONFIG_USE_IRQ=y +diff --git a/configs/ast_g5_phy_defconfig b/configs/ast_g5_phy_defconfig +index 767f3af605..4aefcf49e8 100644 +--- a/configs/ast_g5_phy_defconfig ++++ b/configs/ast_g5_phy_defconfig +@@ -12,3 +12,4 @@ CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y + CONFIG_SYS_NS16550=y ++CONFIG_USE_IRQ=y +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0007-Add-espi-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0007-Add-espi-support.patch new file mode 100644 index 000000000..18cc2f9c8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0007-Add-espi-support.patch @@ -0,0 +1,330 @@ +From 7ad0ea13337550f35c1e726f21d4751bf74078d6 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 10:21:40 -0800 +Subject: [PATCH 2/7] Add espi support + +This adds basic eSPI support for U-Boot. The eSPI driver works best with +interrupts because the timing of the initialization with the PCH is not +trivial. + +The espi driver is currently just a bare-minimum driver allowing the +host to boot. In the future it may be expanded to have further +functions. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Id7072f1408dcf364968b1b74f2192e50a22a82f0 +--- + arch/arm/include/asm/arch-aspeed/regs-scu.h | 2 + + board/aspeed/ast-g5/Makefile | 1 + + board/aspeed/ast-g5/ast-g5-espi.c | 231 ++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-intel.c | 16 ++ + board/aspeed/ast-g5/ast-g5.c | 3 + + 5 files changed, 253 insertions(+) + create mode 100644 board/aspeed/ast-g5/ast-g5-espi.c + create mode 100644 board/aspeed/ast-g5/ast-g5-intel.c + +diff --git a/arch/arm/include/asm/arch-aspeed/regs-scu.h b/arch/arm/include/asm/arch-aspeed/regs-scu.h +index b714fa9234..10b983a966 100644 +--- a/arch/arm/include/asm/arch-aspeed/regs-scu.h ++++ b/arch/arm/include/asm/arch-aspeed/regs-scu.h +@@ -552,6 +552,8 @@ + + #define CLK_25M_IN (0x1 << 23) + ++#define SCU_HW_STRAP_FAST_RESET (1 << 27) ++#define SCU_HW_STRAP_ESPI_ENABLED (1 << 25) + #define SCU_HW_STRAP_2ND_BOOT_WDT (0x1 << 17) + #define SCU_HW_STRAP_SUPER_IO_CONFIG (0x1 << 16) + #define SCU_HW_STRAP_VGA_CLASS_CODE (0x1 << 15) +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index d41b11589f..58e0c648f4 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -1,2 +1,4 @@ + obj-y += ast-g5.o ++obj-y += ast-g5-intel.o ++obj-y += ast-g5-espi.o + obj-y += ast-g5-irq.o +diff --git a/board/aspeed/ast-g5/ast-g5-espi.c b/board/aspeed/ast-g5/ast-g5-espi.c +new file mode 100644 +index 0000000000..79ef253b86 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-espi.c +@@ -0,0 +1,231 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++ ++#define DEBUG_ESPI_ENABLED 1 ++#ifdef DEBUG_ESPI_ENABLED ++#define DBG_ESPI debug ++#else ++#define DBG_ESPI(...) ++#endif ++/* eSPI controller registers */ ++#define ESPI000 0x000 /* Engine Control. */ ++#define ESPI004 0x004 /* Engine Status. */ ++#define ESPI008 0x008 /* Interrupt Status. */ ++#define ESPI00C 0x00C /* Interrupt Enable. */ ++#define ESPI010 0x010 /* DMA Addr of Peripheral Channel Posted Rx pkt */ ++#define ESPI014 0x014 /* Control of Peripheral Channel Posted Rx pkt. */ ++#define ESPI018 0x018 /* Data port of Peripheral Channel Posted Rx pkt. */ ++#define ESPI020 0x020 /* DMA Addr of Peripheral Channel Posted Tx pkt. */ ++#define ESPI024 0x024 /* Control of Peripheral Channel Posted Tx pkt. */ ++#define ESPI028 0x028 /* Data port of Peripheral Channel Posted Tx pkt. */ ++#define ESPI030 0x030 /* DMA Addr of Peripheral Channel Non-Posted Tx pkt. */ ++#define ESPI034 0x034 /* Control of Peripheral Channel Non-Posted Tx pkt. */ ++#define ESPI038 0x038 /* Data port of Peripheral Channel Non-Posted Tx pkt. */ ++#define ESPI040 0x040 /* DMA Addr of OOB Channel Rx pkt. */ ++#define ESPI044 0x044 /* Control of OOB Channel Rx pkt. */ ++#define ESPI048 0x048 /* Data port of OOB Channel Rx pkt. */ ++#define ESPI050 0x050 /* DMA Addr of OOB Channel Tx pkt. */ ++#define ESPI054 0x054 /* Control of OOB Channel Tx pkt. */ ++#define ESPI058 0x058 /* Data port of OOB Channel Tx pkt. */ ++#define ESPI060 0x060 /* DMA Addr of Flash Channel Rx pkt. */ ++#define ESPI064 0x064 /* Control of Flash Channel Rx pkt. */ ++#define ESPI068 0x068 /* Data port of Flash Channel Rx pkt. */ ++#define ESPI070 0x070 /* DMA Addr of Flash Channel Tx pkt. */ ++#define ESPI074 0x074 /* Control of Flash Channel Tx pkt. */ ++#define ESPI078 0x078 /* Data port of Flash Channel Tx pkt. */ ++#define ESPI084 0x084 /* Mapping Src Addr of Peripheral Channel Rx pkt. */ ++#define ESPI088 0x088 /* Mapping Tgt Addr of Peripheral Channel Rx pkt. */ ++#define ESPI08C 0x08C /* Mapping Addr Mask of Peripheral Channel Rx pkt. */ ++#define ESPI090 0x090 /* Mapping Target Addr and Mask of Flash Channel. */ ++#define ESPI094 0x094 /* Interrupt enable of System Event from Master. */ ++#define ESPI098 0x098 /* System Event from and to Master. */ ++#define ESPI09C 0x09C /* GPIO through Virtual Wire Channel. */ ++#define ESPI0A0 0x0A0 /* General Capabilities and Configurations. */ ++#define ESPI0A4 0x0A4 /* Channel 0 Capabilities and Configurations. */ ++#define ESPI0A8 0x0A8 /* Channel 1 Capabilities and Configurations. */ ++#define ESPI0AC 0x0AC /* Channel 2 Capabilities and Configurations. */ ++#define ESPI0B0 0x0B0 /* Channel 3 Capabilities and Configurations. */ ++#define ESPI0B4 0x0B4 /* GPIO Direction of Virtual Wire Channel. */ ++#define ESPI0B8 0x0B8 /* GPIO Selection of Virtual Wire Channel. */ ++#define ESPI0BC 0x0BC /* GPIO Reset Selection of Virtual Wire Channel. */ ++#define ESPI100 0x100 /* Interrupt enable of System Event 1 from Master. */ ++#define ESPI104 0x104 /* System Event 1 from and to Master. */ ++#define ESPI110 0x110 /* Interrupt type 0 of System Event from Master. */ ++#define ESPI114 0x114 /* Interrupt type 1 of System Event from Master. */ ++#define ESPI118 0x118 /* Interrupt type 2 of System Event from Master. */ ++#define ESPI11C 0x11C /* Interrupt status of System Event from Master. */ ++#define ESPI120 0x120 /* Interrupt type 0 of System Event 1 from Master. */ ++#define ESPI124 0x124 /* Interrupt type 1 of System Event 1 from Master. */ ++#define ESPI128 0x128 /* Interrupt type 2 of System Event 1 from Master. */ ++#define ESPI12C 0x12C /* Interrupt status of System Event 1 from Master. */ ++#define ESPICFG004 0x004 /* Device Identification. */ ++#define ESPICFG008 0x008 /* General Capabilities and Configurations. */ ++#define ESPICFG010 0x010 /* Channel 0 Capabilities and Configurations. */ ++#define ESPICFG020 0x020 /* Channel 1 Capabilities and Configurations. */ ++#define ESPICFG030 0x030 /* Channel 2 Capabilities and Configurations. */ ++#define ESPICFG040 0x040 /* Channel 3 Capabilities and Configurations. */ ++#define ESPICFG044 0x044 /* Channel 3 Capabilities and Configurations 2. */ ++#define ESPICFG800 0x800 /* GPIO Direction of Virtual Wire Channel. */ ++#define ESPICFG804 0x804 /* GPIO Selection of Virtual Wire Channel. */ ++#define ESPICFG808 0x808 /* GPIO Reset Selection of Virtual Wire Channel. */ ++#define ESPICFG810 0x810 /* Mapping Src Addr of Peripheral Channel Rx pkt */ ++#define ESPICFG814 0x814 /* Mapping Tgt Addr of Peripheral Channel Rx pkt */ ++#define ESPICFG818 0x818 /* Mapping Addr Mask of Peripheral Channel Rx pkt */ ++ ++/* ESPI000 bits */ ++#define AST_ESPI_OOB_CHRDY (1 << 4) ++#define AST_ESPI_FLASH_SW_CHRDY (0x1 << 7) ++#define AST_ESPI_FLASH_SW_READ (0x1 << 10) ++ ++/* ESPI00C bits (Interrupt Enable) */ ++#define AST_ESPI_IEN_SYS_EV (1 << 8) ++#define AST_ESPI_IEN_GPIO_EV (1 << 9) ++ ++/* ESPI008 bits ISR */ ++#define AST_ESPI_VW_SYS_EVT (1 << 8) ++#define AST_ESPI_VW_SYS_EV1 (1 << 22) ++ ++/* ESPI098 and ESPI11C bits */ ++#define AST_ESPI_OOB_RST_WARN (1 << 6) ++#define AST_ESPI_HOST_RST_WARN (1 << 8) ++#define AST_ESPI_OOB_RST_ACK (1 << 16) ++#define AST_ESPI_SL_BT_DONE (1 << 20) ++#define AST_ESPI_SL_BT_STATUS (1 << 23) ++#define AST_ESPI_HOST_RST_ACK (1 << 27) ++ ++/* ESPI104 bits */ ++#define AST_ESPI_SUS_WARN (1 << 0) ++#define AST_ESPI_SUS_ACK (1 << 20) ++ ++/* LPC chip ID */ ++#define SCR0SIO 0x170 ++#define IRQ_SRC_ESPI 23 /* IRQ 23 */ ++ ++static int espi_irq_handler(struct pt_regs *regs) ++{ ++ uint32_t irq_status = readl(AST_ESPI_BASE + ESPI008); ++ ++ DBG_ESPI("ISR irq_status : 0x%08X\n", irq_status); ++ ++ if (irq_status & AST_ESPI_VW_SYS_EVT) { ++ uint32_t sys_status = readl(AST_ESPI_BASE + ESPI11C); ++ uint32_t sys_event = readl(AST_ESPI_BASE + ESPI098); ++ ++ DBG_ESPI("sys_status : 0x%08X\n", sys_status); ++ if (sys_status & AST_ESPI_HOST_RST_WARN) { ++ DBG_ESPI("HOST_RST_WARN ev: %08X\n", sys_event); ++ if (sys_event & AST_ESPI_HOST_RST_WARN) { ++ uint32_t v = readl(AST_ESPI_BASE + ESPI098) ++ | AST_ESPI_HOST_RST_ACK; ++ writel(v, AST_ESPI_BASE + ESPI098); ++ } ++ } ++ if (sys_status & AST_ESPI_OOB_RST_WARN) { ++ DBG_ESPI("OOB_RST_WARN ev: %08X\n", sys_event); ++ if (sys_event & AST_ESPI_OOB_RST_WARN) { ++ uint32_t v = readl(AST_ESPI_BASE + ESPI098) ++ | AST_ESPI_OOB_RST_ACK; ++ writel(v, AST_ESPI_BASE + ESPI098); ++ } ++ } ++ writel(sys_status, AST_ESPI_BASE + ESPI11C); // clear status ++ } ++ ++ if (irq_status & AST_ESPI_VW_SYS_EV1) { ++ uint32_t sys1_status = readl(AST_ESPI_BASE + ESPI12C); ++ uint32_t sys1_event = readl(AST_ESPI_BASE + ESPI104); ++ ++ DBG_ESPI("sys1_status : 0x%08X\n", sys1_status); ++ if (sys1_status & AST_ESPI_SUS_WARN) { ++ DBG_ESPI("SUS WARN ev: %08X\n", sys1_event); ++ if (sys1_event & AST_ESPI_SUS_WARN) { ++ uint32_t v = readl(AST_ESPI_BASE + ESPI104) ++ | AST_ESPI_SUS_ACK; ++ writel(v, AST_ESPI_BASE + ESPI104); ++ } ++ } ++ writel(sys1_status, AST_ESPI_BASE + ESPI12C); // clear status ++ } ++ writel(irq_status, AST_ESPI_BASE + ESPI008); // clear irq_status ++ return 0; ++} ++ ++static void espi_handshake_ack(void) ++{ ++ // IRQ only serviced if strapped, so no strap check ++ if (!(readl(AST_ESPI_BASE + ESPI098) & AST_ESPI_SL_BT_STATUS)) { ++ DBG_ESPI("Setting espi slave boot done\n"); ++ uint32_t v = readl(AST_ESPI_BASE + ESPI098) ++ | AST_ESPI_SL_BT_STATUS | AST_ESPI_SL_BT_DONE; ++ writel(v, AST_ESPI_BASE + ESPI098); ++ } ++ ++ if (readl(AST_ESPI_BASE + ESPI104) & AST_ESPI_SUS_WARN) { ++ DBG_ESPI("Boot SUS WARN set %08x\n", ++ readl(AST_ESPI_BASE + ESPI104)); ++ uint32_t v = readl(AST_ESPI_BASE + ESPI104) | AST_ESPI_SUS_ACK; ++ writel(v, AST_ESPI_BASE + ESPI104); ++ } ++} ++ ++void espi_init(void) ++{ ++ if (readl(AST_SCU_BASE + AST_SCU_HW_STRAP1) ++ & SCU_HW_STRAP_ESPI_ENABLED) { ++ uint32_t v; ++ DBG_ESPI("espi_init\n"); ++ ++ /* Block flash access from Host */ ++ v = readl(AST_ESPI_BASE + ESPI000) & ~AST_ESPI_FLASH_SW_CHRDY; ++ v |= AST_ESPI_FLASH_SW_READ | AST_ESPI_OOB_CHRDY; ++ writel(v, AST_ESPI_BASE + ESPI000); ++ ++ /* Set SIO register 0x28 to 0xa8 as a faked ASPEED ChipID for ++ * BIOS using in eSPI mode */ ++ v = readl(AST_LPC_BASE + SCR0SIO) & ~0x000000ff; ++ writel(v, AST_LPC_BASE + SCR0SIO); ++ v = readl(AST_LPC_BASE + SCR0SIO) | 0xa8; ++ writel(v, AST_LPC_BASE + SCR0SIO); ++ ++ v = readl(AST_ESPI_BASE + ESPI000) | AST_ESPI_OOB_CHRDY; ++ writel(v, AST_ESPI_BASE + ESPI000); ++ ++ writel(0, AST_ESPI_BASE + ESPI110); ++ writel(0, AST_ESPI_BASE + ESPI114); ++ writel(AST_ESPI_HOST_RST_WARN | AST_ESPI_OOB_RST_WARN, ++ AST_ESPI_BASE + ESPI118); ++ writel(AST_ESPI_HOST_RST_WARN | AST_ESPI_OOB_RST_WARN, ++ AST_ESPI_BASE + ESPI094); ++ ++ writel(AST_ESPI_SUS_WARN, ++ AST_ESPI_BASE + ESPI120); // int type 0 susp warn ++ writel(0, AST_ESPI_BASE + ESPI124); ++ writel(0, AST_ESPI_BASE + ESPI128); ++ writel(AST_ESPI_SUS_WARN, ++ AST_ESPI_BASE ++ + ESPI100); // Enable sysev1 ints for susp warn ++ ++ writel(AST_ESPI_IEN_SYS_EV, ++ AST_ESPI_BASE + ESPI00C); // Enable only sys events ++ ++ espi_handshake_ack(); ++ ++ request_irq(IRQ_SRC_ESPI, espi_irq_handler); ++ } else { ++ DBG_ESPI("No espi strap\n"); ++ } ++} +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +new file mode 100644 +index 0000000000..e79235c8d0 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -0,0 +1,16 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++ ++extern void espi_init(void); ++void ast_g5_intel(void) ++{ ++ espi_init(); ++} +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index 2472aa3603..d41ef9cbd3 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -18,6 +18,8 @@ + + DECLARE_GLOBAL_DATA_PTR; + ++extern void ast_g5_intel(void); ++ + int board_early_init_f(void) + { + /* make sure uart5 is using 24MHz clock */ +@@ -34,6 +36,7 @@ int board_init(void) + gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; + gd->flags = 0; + ++ ast_g5_intel(); + return 0; + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0008-add-sgio-support-for-port80-snoop-post-LEDs.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0008-add-sgio-support-for-port80-snoop-post-LEDs.patch new file mode 100644 index 000000000..cb61bd545 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0008-add-sgio-support-for-port80-snoop-post-LEDs.patch @@ -0,0 +1,160 @@ +From cf43453a75880cf53ea7bbf5859706f2a7cae292 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 12:09:52 -0800 +Subject: [PATCH 3/7] add sgio support for port80 snoop post LEDs + +This ties together the port 80 snooping to the SGPIO output that +ultimately drives the POST code LEDs. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Iaa1b91cd40f4b6323dba0598da373cb631459e66 +--- + arch/arm/include/asm/arch-aspeed/ast_scu.h | 1 + + arch/arm/mach-aspeed/ast-scu.c | 8 ++ + board/aspeed/ast-g5/ast-g5-intel.c | 96 ++++++++++++++++++++++ + 3 files changed, 105 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h +index 98e63351f1..c10e6a9d4b 100644 +--- a/arch/arm/include/asm/arch-aspeed/ast_scu.h ++++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h +@@ -44,6 +44,7 @@ extern u32 ast_scu_get_vga_memsize(void); + extern void ast_scu_init_eth(u8 num); + extern void ast_scu_multi_func_eth(u8 num); + extern void ast_scu_multi_func_romcs(u8 num); ++extern void ast_scu_multi_func_sgpio(void); + + void ast_config_uart5_clk(void); + +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index c83931ed54..63e9c7c167 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -407,6 +407,14 @@ void ast_scu_multi_func_romcs(u8 num) + SCU_FUN_PIN_ROMCS(num), AST_SCU_FUN_PIN_CTRL3); + } + ++void ast_scu_multi_func_sgpio(void) ++{ ++ ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL2) | ++ SCU_FUN_PIN_SGPMI | SCU_FUN_PIN_SGPMO | ++ SCU_FUN_PIN_SGPMLD | SCU_FUN_PIN_SGPMCK, ++ AST_SCU_FUN_PIN_CTRL2); ++} ++ + u32 ast_scu_revision_id(void) + { + int i; +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index e79235c8d0..fca4d91115 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -8,9 +8,105 @@ + */ + + #include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++ ++#define LPC_SNOOP_ADDR 0x80 ++#define HICR5 0x080 /* Host Interface Control Register 5 */ ++#define HICR6 0x084 /* Host Interface Control Register 6 */ ++#define HICR7 0x088 /* Host Interface Control Register 7 */ ++#define HICR8 0x08c /* Host Interface Control Register 8 */ ++#define SNPWADR 0x090 /* LPC Snoop Address Register */ ++#define SNPWDR 0x094 /* LPC Snoop Data Register */ ++#define HICR9 0x098 /* Host Interface Control Register 9 */ ++#define HICRA 0x09c /* Host Interface Control Register A */ ++#define LHCR0 0x0a0 /* LPC Host Control Register 0 */ ++#define LHCR1 0x0a4 /* LPC Host Control Register 1 */ ++#define LHCR2 0x0a8 /* LPC Host Control Register 2 */ ++#define LHCR3 0x0ac /* LPC Host Control Register 3 */ ++#define LHCR4 0x0b0 /* LPC Host Control Register 4 */ ++#define LHCR5 0x0b4 /* LPC Host Control Register 5 */ ++#define LHCR6 0x0b8 /* LPC Host Control Register 6 */ ++#define LHCR7 0x0bc /* LPC Host Control Register 7 */ ++#define LHCR8 0x0c0 /* LPC Host Control Register 8 */ ++#define PCCR6 0x0c4 /* Post Code Control Register 6 */ ++#define LHCRA 0x0c8 /* LPC Host Control Register A */ ++#define LHCRB 0x0cc /* LPC Host Control Register B */ ++#define PCCR4 0x0d0 /* Post Code Control Register 4 */ ++#define PCCR5 0x0d4 /* Post Code Control Register 5 */ ++#define HICRB 0x100 /* Host Interface Control Register B */ ++#define HICRC 0x104 /* Host Interface Control Register C */ ++/* HICR5 Bits */ ++#define HICR5_EN_SIOGIO (1 << 31) /* Enable SIOGIO */ ++#define HICR5_EN80HGIO (1 << 30) /* Enable 80hGIO */ ++#define HICR5_SEL80HGIO (0x1f << 24) /* Select 80hGIO */ ++#define SET_SEL80HGIO(x) ((x & 0x1f) << 24) /* Select 80hGIO Offset */ ++#define HICR5_UNKVAL_MASK 0x1FFF0000 /* Bits with unknown values on reset */ ++#define HICR5_ENINT_SNP0W (1 << 1) /* Enable Snooping address 0 */ ++#define HICR5_EN_SNP0W (1 << 0) /* Enable Snooping address 0 */ ++ ++/* HRCR6 Bits */ ++#define HICR6_STR_SNP0W (1 << 0) /* Interrupt Status Snoop address 0 */ ++#define HICR6_STR_SNP1W (1 << 1) /* Interrupt Status Snoop address 1 */ ++ ++/* HICRB Bits */ ++#define HICRB_EN80HSGIO (1 << 13) /* Enable 80hSGIO */ ++ ++#define SGPIO_CLK_DIV(N) ((N) << 16) ++#define SGPIO_BYTES(N) ((N) << 6) ++#define SGPIO_ENABLE 1 ++#define GPIO254 0x254 ++ ++static void sgpio_init(void) ++{ ++ uint32_t value; ++ /* ++ 33.4.2 ++ LPC port80h direct to SGPIO ++ In AST2500 SGPIO, it supports output data from 80h. It always uses SGPIOA. ++ 1. Configure LPC snoop function. ++ (a) Set SNPWADR(0x1e789090)[15:0] to 0x80. ++ (b) Set HICR5(0x1e789080)[0] to 1 to enable snoop. ++ 2. Configure SGPIO ++ (a) Set GPIO254[9:6] to larger than or equal to 0x1. ++ (b) Set GPIO254[0] to 1 to enable SGPIO. ++ 3. Set SuperIO ++ (a) Set SIOR7 30h to 0x40. ++ */ ++ /* make sure multi-pin stuff goes in our favor */ ++ ast_scu_multi_func_sgpio(); ++ ++ /* set lpc snoop #0 to port 0x80 */ ++ value = readl(AST_LPC_BASE + SNPWADR) & 0xffff0000; ++ writel(value | LPC_SNOOP_ADDR, AST_LPC_BASE + SNPWADR); ++ ++ /* clear interrupt status */ ++ value = readl(AST_LPC_BASE + HICR6); ++ value |= HICR6_STR_SNP0W | HICR6_STR_SNP1W; ++ writel(value, AST_LPC_BASE + HICR6); ++ ++ /* enable lpc snoop #0 and SIOGIO */ ++ value = readl(AST_LPC_BASE + HICR5) & ~(HICR5_UNKVAL_MASK); ++ value |= HICR5_EN_SIOGIO | HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; ++ writel(value, AST_LPC_BASE + HICR5); ++ ++ ++ /* enable port80h snoop on SGPIO */ ++ value = readl(AST_LPC_BASE + HICRB) | HICRB_EN80HSGIO; ++ writel(value, AST_LPC_BASE + HICRB); ++ ++ /* set the gpio clock to pclk/(2*(5+1)) or ~2 MHz */ ++ value = SGPIO_CLK_DIV(256) | SGPIO_BYTES(10) | SGPIO_ENABLE; ++ writel(value, AST_GPIO_BASE + GPIO254); ++} + + extern void espi_init(void); + void ast_g5_intel(void) + { + espi_init(); ++ sgpio_init(); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0009-Add-basic-GPIO-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0009-Add-basic-GPIO-support.patch new file mode 100644 index 000000000..a49f196ac --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0009-Add-basic-GPIO-support.patch @@ -0,0 +1,414 @@ +From 89728d8c255204c8d9ec46a1dc0d412b04708f22 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 09:58:01 -0800 +Subject: [PATCH 4/7] Add basic GPIO support + +Add a table of well-known gpios (such as FP LEDs and FF UPD jumper) and +initialize them at boot. + +Add a mechanism to get/set well known gpios from command line. + +Change-Id: I4136a5ccb048b3604f13b17ea0c18a4bc596c249 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + board/aspeed/ast-g5/Makefile | 1 + + board/aspeed/ast-g5/ast-g5-gpio.c | 195 +++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-gpio.h | 102 +++++++++++++++ + board/aspeed/ast-g5/ast-g5-intel.c | 42 +++++++ + board/aspeed/ast-g5/ast-g5.h | 1 + + 5 files changed, 341 insertions(+) + create mode 100644 board/aspeed/ast-g5/ast-g5-gpio.c + create mode 100644 board/aspeed/ast-g5/ast-g5-gpio.h + +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index 58e0c648f4..2970ae5741 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -2,3 +2,4 @@ obj-y += ast-g5.o + obj-y += ast-g5-intel.o + obj-y += ast-g5-espi.o + obj-y += ast-g5-irq.o ++obj-y += ast-g5-gpio.o +diff --git a/board/aspeed/ast-g5/ast-g5-gpio.c b/board/aspeed/ast-g5/ast-g5-gpio.c +new file mode 100644 +index 0000000000..d596c15914 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-gpio.c +@@ -0,0 +1,195 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++#include "ast-g5-gpio.h" ++ ++typedef struct _gpio_bases { ++ uint32_t u32ddr; /* data and direction registers */ ++ uint32_t u32intcfg; /* interrupt config */ ++ uint32_t u32debounce; /* debounce config */ ++ uint32_t u32cmdsrc; /* command source config */ ++} sGPIO_BASES; ++ ++static const sGPIO_BASES GPIO_BASES[] = { ++ /* ABCD */ ++ {AST_GPIO_BASE + 0x0000, AST_GPIO_BASE + 0x0008, ++ AST_GPIO_BASE + 0x0040, AST_GPIO_BASE + 0x0060}, ++ /* EFGH */ ++ {AST_GPIO_BASE + 0x0020, AST_GPIO_BASE + 0x0028, ++ AST_GPIO_BASE + 0x0048, AST_GPIO_BASE + 0x0068}, ++ /* IJKL */ ++ {AST_GPIO_BASE + 0x0070, AST_GPIO_BASE + 0x0098, ++ AST_GPIO_BASE + 0x00b0, AST_GPIO_BASE + 0x0090}, ++ /* MNOP */ ++ {AST_GPIO_BASE + 0x0078, AST_GPIO_BASE + 0x00e8, ++ AST_GPIO_BASE + 0x0100, AST_GPIO_BASE + 0x00e0}, ++ /* QRST */ ++ {AST_GPIO_BASE + 0x0080, AST_GPIO_BASE + 0x0118, ++ AST_GPIO_BASE + 0x0130, AST_GPIO_BASE + 0x0110}, ++ /* UVWX */ ++ {AST_GPIO_BASE + 0x0088, AST_GPIO_BASE + 0x0148, ++ AST_GPIO_BASE + 0x0160, AST_GPIO_BASE + 0x0140}, ++ /* YZAB */ ++ {AST_GPIO_BASE + 0x01e0, AST_GPIO_BASE + 0x0178, ++ AST_GPIO_BASE + 0x0190, AST_GPIO_BASE + 0x0170}, ++ /* AC__ */ ++ {AST_GPIO_BASE + 0x01e8, AST_GPIO_BASE + 0x01a8, ++ AST_GPIO_BASE + 0x01c0, AST_GPIO_BASE + 0x01a0}, ++}; ++ ++static size_t gpio_max = 0; ++static const GPIOValue * gpio_table = NULL; ++ ++void gpio_set_value(int n, int asserted) ++{ ++ uint8_t port; ++ uint8_t pin; ++ uint32_t base; ++ uint8_t shift; ++ uint8_t assert; ++ uint32_t gpio_value; ++ ++ if (n >= gpio_max || !gpio_table) { ++ return; ++ } ++ port = GPIO_PORT(gpio_table[n].u8PortPin); ++ assert = GPIO_PORT(gpio_table[n].u8Value); ++ pin = GPIO_PIN(gpio_table[n].u8PortPin); ++ base = GPIO_BASES[GPIO_GROUP(port)].u32ddr; ++ shift = GPIO_SHIFT(port, pin); ++ ++ gpio_value = readl(base + GPIO_DATA_VALUE); ++ if ((assert &&asserted) || !(assert || asserted)) { ++ // set the bit ++ gpio_value |= (1 << shift); ++ } else { ++ // clear the bit ++ gpio_value &= ~(1 << shift); ++ } ++ writel(gpio_value, base + GPIO_DATA_VALUE); ++} ++ ++int gpio_get_value(int n) ++{ ++ uint8_t port; ++ uint8_t pin; ++ uint32_t base; ++ uint8_t shift; ++ uint8_t assert; ++ uint32_t gpio_value; ++ ++ if (n >= gpio_max || !gpio_table) { ++ return -1; ++ } ++ port = GPIO_PORT(gpio_table[n].u8PortPin); ++ assert = GPIO_PORT(gpio_table[n].u8Value); ++ pin = GPIO_PIN(gpio_table[n].u8PortPin); ++ base = GPIO_BASES[GPIO_GROUP(port)].u32ddr; ++ shift = GPIO_SHIFT(port, pin); ++ ++ gpio_value = readl(base + GPIO_DATA_VALUE); ++ gpio_value >>= shift; ++ gpio_value &= 1; ++ gpio_value ^= assert; ++ return !gpio_value; ++} ++ ++void gpio_init(const GPIOValue* table, size_t count) ++{ ++ uint32_t pclk, value; ++ int i; ++ ++ gpio_table = table; ++ gpio_max = count; ++ /* set up the debounce timers (in units of PCLK cycles) */ ++ pclk = ast_get_ahbclk(); ++ /* GPIO_DEBOUNCE_120us */ ++ writel((pclk / 1000000) * 120, AST_GPIO_BASE + GPIO_DEBOUNCE_TIMER_0); ++ /* GPIO_DEBOUNCE_8ms */ ++ writel((pclk / 1000000) * 8000, AST_GPIO_BASE + GPIO_DEBOUNCE_TIMER_1); ++ /* GPIO_DEBOUNCE_16ms */ ++ writel((pclk / 1000000) * 16000, AST_GPIO_BASE + GPIO_DEBOUNCE_TIMER_2); ++ ++ for (i = 0; i < gpio_max; i++) { ++ uint8_t port; ++ uint8_t pin; ++ uint32_t base; ++ uint8_t shift; ++ ++ port = GPIO_PORT(gpio_table[i].u8PortPin); ++ pin = GPIO_PIN(gpio_table[i].u8PortPin); ++ base = GPIO_BASES[GPIO_GROUP(port)].u32ddr; ++ shift = GPIO_SHIFT(port, pin); ++ ++ /* set direction */ ++ value = readl(base + GPIO_DIRECTION); ++ if (gpio_table[i].u8PinCFG & GPCFG_OUTPUT_EN) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DIRECTION); ++ ++ /* set data value */ ++ value = readl(base + GPIO_DATA_VALUE); ++ if (gpio_table[i].u8Value) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DATA_VALUE); ++ ++ /* set debounce */ ++ base = GPIO_BASES[GPIO_GROUP(port)].u32debounce; ++ value = readl(base + GPIO_DEBOUNCE_SEL_0); ++ if (gpio_table[i].u8Debounce & 0x01) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DEBOUNCE_SEL_0); ++ value = readl(base + GPIO_DEBOUNCE_SEL_1); ++ if (gpio_table[i].u8Debounce & 0x02) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DEBOUNCE_SEL_1); ++ } ++} ++ ++int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int n; ++ if (argc < 3) { ++ return 1; ++ } ++ n = simple_strtoul(argv[2], NULL, 16); ++ if (argv[1][0] == 'g') { ++ printf("%d\n", gpio_get_value(n)); ++ return 0; ++ } ++ if (argc < 4) { ++ return 1; ++ } ++ if (argv[1][0] == 's') { ++ int value; ++ value = simple_strtoul(argv[3], NULL, 16); ++ gpio_set_value(n, value); ++ return 0; ++ } ++ ++ return 1; ++} ++U_BOOT_CMD(gpio, 4, 0, do_gpio, ++ "do stuff with gpios <set|get> [n] [value]", ++ ""); +diff --git a/board/aspeed/ast-g5/ast-g5-gpio.h b/board/aspeed/ast-g5/ast-g5-gpio.h +new file mode 100644 +index 0000000000..a820c0fcad +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-gpio.h +@@ -0,0 +1,102 @@ ++#ifndef __HW_GPIO_H__ ++#define __HW_GPIO_H__ ++ ++#define GPIO_PORT_A 0 ++#define GPIO_PORT_B 1 ++#define GPIO_PORT_C 2 ++#define GPIO_PORT_D 3 ++#define GPIO_PORT_E 4 ++#define GPIO_PORT_F 5 ++#define GPIO_PORT_G 6 ++#define GPIO_PORT_H 7 ++#define GPIO_PORT_I 8 ++#define GPIO_PORT_J 9 ++#define GPIO_PORT_K 10 ++#define GPIO_PORT_L 11 ++#define GPIO_PORT_M 12 ++#define GPIO_PORT_N 13 ++#define GPIO_PORT_O 14 ++#define GPIO_PORT_P 15 ++#define GPIO_PORT_Q 16 ++#define GPIO_PORT_R 17 ++#define GPIO_PORT_S 18 ++#define GPIO_PORT_T 19 ++#define GPIO_PORT_U 20 ++#define GPIO_PORT_V 21 ++#define GPIO_PORT_W 22 ++#define GPIO_PORT_X 23 ++#define GPIO_PORT_Y 24 ++#define GPIO_PORT_Z 25 ++#define GPIO_PORT_AA 26 ++#define GPIO_PORT_AB 27 ++#define GPIO_PORT_AC 28 ++ ++#define GPIO_PIN_0 0 ++#define GPIO_PIN_1 1 ++#define GPIO_PIN_2 2 ++#define GPIO_PIN_3 3 ++#define GPIO_PIN_4 4 ++#define GPIO_PIN_5 5 ++#define GPIO_PIN_6 6 ++#define GPIO_PIN_7 7 ++ ++#define GPIO_DEBOUNCE_TIMER_0 0x50 ++#define GPIO_DEBOUNCE_TIMER_1 0x54 ++#define GPIO_DEBOUNCE_TIMER_2 0x58 ++ ++/* relative to u32ddr base */ ++#define GPIO_DATA_VALUE 0x00 ++#define GPIO_DIRECTION 0x04 ++/* relative to u32intcfg base */ ++#define GPIO_INT_ENABLE 0x00 ++#define GPIO_INT_SENSE0 0x04 ++#define GPIO_INT_SENSE1 0x18 ++#define GPIO_INT_SENSE2 0x1c ++#define GPIO_INT_STATUS 0x20 ++#define GPIO_RESET_TOL 0x24 ++/* relative to u32debounce base */ ++#define GPIO_DEBOUNCE_SEL_0 0 ++#define GPIO_DEBOUNCE_SEL_1 4 ++/* relative to u32cmdsrc base */ ++#define GPIO_CMD_SRC_0 0 ++#define GPIO_CMD_SRC_1 4 ++ ++#define PORT_PIN(PORT, PIN) (((PORT) << 3) | ((PIN)&0x07)) ++#define GPIO_PIN(N) (N & 0x07) ++#define GPIO_PORT(N) (N >> 3) ++#define GPIO_SHIFT(PORT, PIN) ((PIN) + (((PORT) % 4) * 8)) ++#define GPIO_GROUP(PORT) ((PORT) / 4) ++ ++#define ID_LED_PORT_PIN PORT_PIN(GPIO_PORT_S, GPIO_PIN_6) ++#define GRN_LED_PORT_PIN PORT_PIN(GPIO_PORT_S, GPIO_PIN_4) ++#define AMB_LED_PORT_PIN PORT_PIN(GPIO_PORT_S, GPIO_PIN_5) ++#define FORCE_BMC_UPDATE_PORT_PIN PORT_PIN(GPIO_PORT_D, GPIO_PIN_0) ++#define TPM_EN_PULSE_PORT_PIN PORT_PIN(GPIO_PORT_D, GPIO_PIN_6) ++ ++ ++// GPIO Configuration Register bits ++#define GPCFG_EVENT_TO_SMI (1 << 7) // 1 == enabled ++#define GPCFG_EVENT_TO_IRQ (1 << 6) // 1 == enabled ++#define GPCFG_DEBOUNCE_EN (1 << 5) // 1 == input debounce, 0 == pulse output ++#define GPCFG_ACTIVE_HIGH (1 << 4) // 1 == Active high ++#define GPCFG_LEVEL_TRIG (1 << 3) // 1 == level (default), 0 == edge ++#define GPCFG_OUTPUT_EN (1 << 0) // 1 == Output enabled ++ ++// GPIO Debounce and Blink Configuration Register bits ++#define GPIO_DEBOUNCE_NONE 0x00 ++#define GPIO_DEBOUNCE_60US 0x01 ++#define GPIO_DEBOUNCE_8MS 0x02 ++#define GPIO_DEBOUNCE_16MS 0x03 ++ ++typedef struct { ++ uint8_t u8PortPin; ++ uint8_t u8PinCFG; ++ uint8_t u8Value; ++ uint8_t u8Debounce; ++} GPIOValue; ++ ++void gpio_init(const GPIOValue* table, size_t count); ++void gpio_set_value(int n, int asserted); ++int gpio_get_value(int n); ++ ++#endif /* __HW_GPIO_H__ */ +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index fca4d91115..252a05dd73 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -14,6 +14,47 @@ + #include <asm/arch/aspeed.h> + + #include "ast-g5.h" ++#include "ast-g5-gpio.h" ++ ++/* Names to match the GPIOs */ ++enum gpio_names { ++ GPIO_ID_LED = 0, ++ GPIO_GREEN_LED, ++ GPIO_AMBER_LED, ++ GPIO_FF_UPD_JUMPER, ++ GPIO_ENABLE_TPM_PULSE, ++}; ++ ++#define GPIO_CFG_DEFAULT (GPCFG_ACTIVE_HIGH | GPCFG_LEVEL_TRIG) ++// Active High, Level, Output Disabled ++ ++#define GPIO_CFG_FP_LED (GPCFG_OUTPUT_EN) ++// Active High, Pull-up, Level, Output Disabled ++ ++// Format is: ++// GPIO PORT, GPIO PIN Number, GPIO PIN Configuration, GPIO PIN Value, GPIO ++// Debounce/Blink Setting ++static const GPIOValue gpio_table[] = { ++ /* ID LED pin S6 - low asserted, 0=on */ ++ [GPIO_ID_LED] = {ID_LED_PORT_PIN, GPIO_CFG_FP_LED, 0, ++ GPIO_DEBOUNCE_NONE}, ++ ++ /* Green LED pin S4 - high asserted, 0=off */ ++ [GPIO_GREEN_LED] = {GRN_LED_PORT_PIN, GPIO_CFG_FP_LED, 1, ++ GPIO_DEBOUNCE_NONE}, ++ ++ /* Amber LED pin S5 - high asserted, 0=off */ ++ [GPIO_AMBER_LED] = {AMB_LED_PORT_PIN, GPIO_CFG_FP_LED, 1, ++ GPIO_DEBOUNCE_NONE}, ++ ++ /* Force Update Jumper -- pin D0 */ ++ [GPIO_FF_UPD_JUMPER] = {FORCE_BMC_UPDATE_PORT_PIN, GPIO_CFG_DEFAULT, 0, ++ GPIO_DEBOUNCE_8MS}, ++ ++ /* Enable Pulse -- pin D6 */ ++ [GPIO_ENABLE_TPM_PULSE] = {PORT_PIN(GPIO_PORT_D, GPIO_PIN_6), ++ GPIO_CFG_DEFAULT, 0, GPIO_DEBOUNCE_8MS}, ++}; + + #define LPC_SNOOP_ADDR 0x80 + #define HICR5 0x080 /* Host Interface Control Register 5 */ +@@ -107,6 +148,7 @@ static void sgpio_init(void) + extern void espi_init(void); + void ast_g5_intel(void) + { ++ gpio_init(gpio_table, ARRAY_SIZE(gpio_table)); + espi_init(); + sgpio_init(); + } +diff --git a/board/aspeed/ast-g5/ast-g5.h b/board/aspeed/ast-g5/ast-g5.h +index 9fd10eccb3..908db1477b 100644 +--- a/board/aspeed/ast-g5/ast-g5.h ++++ b/board/aspeed/ast-g5/ast-g5.h +@@ -3,5 +3,6 @@ + + #include <common.h> + #include "ast-g5-irq.h" ++#include "ast-g5-gpio.h" + + #endif /* _AST_G5_H_ */ +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch new file mode 100644 index 000000000..c97e6d74f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch @@ -0,0 +1,98 @@ +From 1f710737f2fe8dea4bc5ebef1e6011de294764b4 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 14:59:04 -0800 +Subject: [PATCH] Update Force Firmware Update Jumper to use new gpio API + +Add a function that allows easy reading of the FFUJ from other +functions, such as autoboot. + +Change-Id: I8ead931e9dd828522095a0ef386875be652ec885 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + .../include/asm/arch-aspeed/ast-g5-intel.h | 19 +++++++++++++++++++ + arch/arm/include/asm/arch-aspeed/platform.h | 1 + + board/aspeed/ast-g5/ast-g5-intel.c | 5 +++++ + common/autoboot.c | 6 ++++++ + 4 files changed, 31 insertions(+) + create mode 100644 arch/arm/include/asm/arch-aspeed/ast-g5-intel.h + +diff --git a/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h b/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h +new file mode 100644 +index 0000000000..cd9a0994fa +--- /dev/null ++++ b/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __AST_INTEL_G5_H__ ++#define __AST_INTEL_G5_H__ ++ ++#define AST_G5_INTEL 1 ++ ++#ifndef __ASSEMBLY__ ++int intel_force_firmware_jumper_enabled(void); ++#endif ++ ++#endif /* __AST_INTEL_G5_H__ */ +diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h +index 9f339e913a..3ea1c99089 100644 +--- a/arch/arm/include/asm/arch-aspeed/platform.h ++++ b/arch/arm/include/asm/arch-aspeed/platform.h +@@ -27,6 +27,7 @@ + #include <asm/arch/ast2400_platform.h> + #elif defined(AST_SOC_G5) + #include <asm/arch/ast_g5_platform.h> ++#include <asm/arch/ast-g5-intel.h> + #define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_early_init_f */ + #else + #err "No define for platform.h" +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 252a05dd73..58ad6a55b8 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -145,6 +145,11 @@ static void sgpio_init(void) + writel(value, AST_GPIO_BASE + GPIO254); + } + ++int intel_force_firmware_jumper_enabled(void) ++{ ++ return gpio_get_value(GPIO_FF_UPD_JUMPER); ++} ++ + extern void espi_init(void); + void ast_g5_intel(void) + { +diff --git a/common/autoboot.c b/common/autoboot.c +index c52bad84a4..d66c0fa63a 100644 +--- a/common/autoboot.c ++++ b/common/autoboot.c +@@ -14,6 +14,7 @@ + #include <menu.h> + #include <post.h> + #include <u-boot/sha256.h> ++#include <asm/arch/platform.h> + + DECLARE_GLOBAL_DATA_PTR; + +@@ -259,6 +260,11 @@ static int abortboot(int bootdelay) + { + int abort = 0; + ++# ifdef AST_G5_INTEL ++ if (intel_force_firmware_jumper_enabled()) ++ return 1; ++# endif ++ + if (bootdelay >= 0) + abort = __abortboot(bootdelay); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch new file mode 100644 index 000000000..11a474b96 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch @@ -0,0 +1,160 @@ +From 83d67b5b3cbffcefda5efdc0060b9e30f44c9aca Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 14:44:49 -0800 +Subject: [PATCH 6/7] Add basic timer support for Aspeed g5 in U-Boot + +Timers will be used for timing events and making blinky LEDs. This just +adds the API and infrastructure. + +Change-Id: I8ff03b26070b43a47fb970ddf6124d6c3f29b058 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + board/aspeed/ast-g5/Makefile | 1 + + board/aspeed/ast-g5/ast-g5-intel.c | 1 + + board/aspeed/ast-g5/ast-g5-timer.c | 66 ++++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-timer.h | 27 ++++++++++++ + board/aspeed/ast-g5/ast-g5.h | 1 + + 5 files changed, 96 insertions(+) + create mode 100644 board/aspeed/ast-g5/ast-g5-timer.c + create mode 100644 board/aspeed/ast-g5/ast-g5-timer.h + +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index 2970ae5741..90224333c4 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -3,3 +3,4 @@ obj-y += ast-g5-intel.o + obj-y += ast-g5-espi.o + obj-y += ast-g5-irq.o + obj-y += ast-g5-gpio.o ++obj-y += ast-g5-timer.o +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 58ad6a55b8..23bf4e4352 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -15,6 +15,7 @@ + + #include "ast-g5.h" + #include "ast-g5-gpio.h" ++#include "ast-g5-timer.h" + + /* Names to match the GPIOs */ + enum gpio_names { +diff --git a/board/aspeed/ast-g5/ast-g5-timer.c b/board/aspeed/ast-g5/ast-g5-timer.c +new file mode 100644 +index 0000000000..56157222d9 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-timer.c +@@ -0,0 +1,66 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++#include "ast-g5-timer.h" ++#include "ast-g5-irq.h" ++ ++static const int timer_irqs[] = {16, 17, 18, 35, 37, 37, 38, 39}; ++/* offsets from AST_TIMER_BASE for each timer */ ++static const uint32_t timer_bases[] = {0, 0x10, 0x20, 0x40, ++ 0x50, 0x60, 0x70, 0x80}; ++#define TIMER_1MHZ_CLK_COUNT 1000000u ++#define TIMER_ENABLE 1 ++#define TIMER_1MHZ_CLK_SEL 2 ++#define TIMER_ENABLE_IRQ 4 ++#define TIMER_ENABLE_PULSE 8 ++#define TIMER_CONTROL 0x30 ++#define TIMER_RELOAD 0x04 ++ ++void timer_enable(int n, uint32_t freq, interrupt_handler_t *handler) ++{ ++ if (n < 0 || n > 7) { ++ return; ++ } ++ uint32_t tctrl = readl(AST_TIMER_BASE + TIMER_CONTROL); ++ writel(tctrl & ~(0x0f << (n * 4)), AST_TIMER_BASE + TIMER_CONTROL); ++ ++ // figure out best base for requested frequency ++ // (this will give 1MHz clock preference if period is within 1ms of ++ // requested) ++ uint32_t v = TIMER_1MHZ_CLK_COUNT / freq; ++ if (v > 1000 || v * freq == TIMER_1MHZ_CLK_COUNT) { ++ tctrl |= (TIMER_1MHZ_CLK_SEL << (n * 4)); ++ } else { ++ uint32_t pclk = ast_get_ahbclk(); ++ v = pclk / freq; ++ } ++ writel(v, AST_TIMER_BASE + timer_bases[n] + TIMER_RELOAD); ++ if (handler) { ++ request_irq(timer_irqs[n], handler); ++ tctrl |= (TIMER_ENABLE_IRQ << (n * 4)); ++ } ++ tctrl |= (TIMER_ENABLE << (n * 4)); ++ writel(tctrl, AST_TIMER_BASE + TIMER_CONTROL); ++} ++ ++void timer_disable(int n) ++{ ++ if (n < 0 || n > 7) { ++ return; ++ } ++ uint32_t tctrl = readl(AST_TIMER_BASE + TIMER_CONTROL); ++ writel(tctrl & ~(0x0f << (n * 4)), AST_TIMER_BASE + TIMER_CONTROL); ++} +diff --git a/board/aspeed/ast-g5/ast-g5-timer.h b/board/aspeed/ast-g5/ast-g5-timer.h +new file mode 100644 +index 0000000000..4b1ac28a9f +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-timer.h +@@ -0,0 +1,27 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __AST_G5_TIMER_H__ ++#define __AST_G5_TIMER_H__ ++ ++#include <common.h> ++ ++#define TIMER_1 0 ++#define TIMER_2 1 ++#define TIMER_3 2 ++#define TIMER_4 3 ++#define TIMER_5 4 ++#define TIMER_6 5 ++#define TIMER_7 6 ++#define TIMER_8 7 ++ ++void timer_enable(int n, uint32_t freq, interrupt_handler_t handler); ++void timer_disable(int n); ++ ++#endif /* __AST_G5_TIMER_H__ */ +diff --git a/board/aspeed/ast-g5/ast-g5.h b/board/aspeed/ast-g5/ast-g5.h +index 908db1477b..28fe5eafcb 100644 +--- a/board/aspeed/ast-g5/ast-g5.h ++++ b/board/aspeed/ast-g5/ast-g5.h +@@ -4,5 +4,6 @@ + #include <common.h> + #include "ast-g5-irq.h" + #include "ast-g5-gpio.h" ++#include "ast-g5-timer.h" + + #endif /* _AST_G5_H_ */ +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0012-Add-status-and-ID-LED-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0012-Add-status-and-ID-LED-support.patch new file mode 100644 index 000000000..5a2c2206f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0012-Add-status-and-ID-LED-support.patch @@ -0,0 +1,144 @@ +From f0e3631ea3005640f988727f051106d83b5dfdaf Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 12:16:53 -0800 +Subject: [PATCH 7/7] Add status and ID LED support + +Add status (amber and green) and ID (blue) LED support. In the +bootloader phase, the LEDs should be blinking. When booting linux, they +should turn to a fixed state. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Ic9595621b21000ef465ff57ed2047855296e2714 +--- + board/aspeed/ast-g5/ast-g5-intel.c | 118 +++++++++++++++++++++++++++++ + 1 file changed, 118 insertions(+) + +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 23bf4e4352..5ff2cbd0e2 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -146,6 +146,110 @@ static void sgpio_init(void) + writel(value, AST_GPIO_BASE + GPIO254); + } + ++/* running the timer at 48 hertz will easily give a 24Hz blink */ ++#define TICK_HZ 48 ++#define BLINK_DELAY(HZ) ((int)((TICK_HZ / (HZ)) / 2) - 1) ++typedef enum { ++ /* ++ * Identifies the control request for the ID LED. ++ */ ++ EIDLED_Initialize = 0, ++ EIDLED_Tick, ++ EIDLED_Toggle, ++ EIDLED_Off, ++ EIDLED_On, ++ EIDLED_Blink, ++ EIDLED_Blink_24HZ = EIDLED_Blink + BLINK_DELAY(24), ++ EIDLED_Blink_12HZ = EIDLED_Blink + BLINK_DELAY(12), ++ EIDLED_Blink_6HZ = EIDLED_Blink + BLINK_DELAY(6), ++ EIDLED_Blink_3HZ = EIDLED_Blink + BLINK_DELAY(3), ++ EIDLED_Blink_1HZ = EIDLED_Blink + BLINK_DELAY(1), ++ EIDLED_Blink_0_5HZ = EIDLED_Blink + BLINK_DELAY(0.5), ++} EIDLEDControl; ++ ++struct led_info { ++ int gpio; ++ EIDLEDControl mode; ++ int count; ++ int state; ++}; ++ ++static struct led_info s_led_info[] = { ++ [GPIO_ID_LED] = {GPIO_ID_LED, EIDLED_Blink_3HZ, 1, 0}, ++ [GPIO_GREEN_LED] = {GPIO_GREEN_LED, EIDLED_Off, 0, 0}, ++ [GPIO_AMBER_LED] = {GPIO_AMBER_LED, EIDLED_Off, 0, 0}, ++}; ++ ++extern void gpio_set_value(int n, int asserted); ++void id_led_control(int id, int action) ++{ ++ if (id >= ARRAY_SIZE(s_led_info)) { ++ return; ++ } ++ /* don't bother with LEDs that are not initialized */ ++ if (EIDLED_Initialize == s_led_info[id].mode) { ++ return; ++ } ++ ++ /* check for a blinker action */ ++ if (EIDLED_Tick == action) { ++ if (s_led_info[id].mode < EIDLED_Blink) { ++ return; ++ } ++ /* check countdown for blink */ ++ if (s_led_info[id].count == 0) { ++ s_led_info[id].count = ++ s_led_info[id].mode - EIDLED_Blink; ++ s_led_info[id].state = !s_led_info[id].state; ++ } else { ++ s_led_info[id].count--; ++ return; ++ } ++ } else if (EIDLED_Toggle == action) { ++ s_led_info[id].state = !s_led_info[id].state; ++ s_led_info[id].mode = ++ s_led_info[id].state ? EIDLED_On : EIDLED_Off; ++ } else if (action > EIDLED_Toggle) { ++ s_led_info[id].mode = action; ++ if (EIDLED_Off == action) { ++ s_led_info[id].state = 0; ++ } else if (EIDLED_On == action) { ++ s_led_info[id].state = 1; ++ } else if (action >= EIDLED_Blink) { ++ s_led_info[id].count = action - EIDLED_Blink; ++ /* wait for the next tick */ ++ return; ++ } ++ } else if (EIDLED_Initialize == action) { ++ if (s_led_info[id].mode >= EIDLED_Blink) { ++ s_led_info[id].count = ++ s_led_info[id].mode - EIDLED_Blink; ++ } ++ } ++ gpio_set_value(s_led_info[id].gpio, s_led_info[id].state); ++} ++ ++static void timer8_irq_handler(void *regs) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(s_led_info); i++) { ++ id_led_control(i, EIDLED_Tick); ++ } ++} ++ ++void timer8_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(s_led_info); i++) { ++ id_led_control(i, EIDLED_Initialize); ++ } ++ ++ /* set up the timer to fire at TICK_HZ HZ */ ++ timer_enable(TIMER_8, TICK_HZ, timer8_irq_handler); ++} ++ + int intel_force_firmware_jumper_enabled(void) + { + return gpio_get_value(GPIO_FF_UPD_JUMPER); +@@ -157,4 +269,10 @@ void ast_g5_intel(void) + gpio_init(gpio_table, ARRAY_SIZE(gpio_table)); + espi_init(); + sgpio_init(); ++ timer8_init(); ++ if (intel_force_firmware_jumper_enabled()) { ++ id_led_control(GPIO_AMBER_LED, EIDLED_On); ++ } else { ++ id_led_control(GPIO_GREEN_LED, EIDLED_On); ++ } + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0013-aspeed-Add-Pwm-Driver.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0013-aspeed-Add-Pwm-Driver.patch new file mode 100644 index 000000000..d235aea62 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0013-aspeed-Add-Pwm-Driver.patch @@ -0,0 +1,132 @@ +From 3134584998f624bb6c4ee11102b0bd9b7bb1cbba Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 15:57:57 -0800 +Subject: [PATCH 1/1] aspeed: add Pwm Driver + +Change-Id: Ia8b80212f7c70aafcc6a71782936ec95cf9b7f38 +--- + board/aspeed/ast-g5/ast-g5-intel.c | 105 +++++++++++++++++++++++++++++ + 1 file changed, 105 insertions(+) + +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 5ff2cbd0e2..f810ded4e7 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -263,9 +263,114 @@ int intel_force_firmware_jumper_enabled(void) + return gpio_get_value(GPIO_FF_UPD_JUMPER); + } + ++/* PWM offsets */ ++ ++#define PWM_BASE_ADDR 0x1E786000 ++#define PWM_CONTROL 0x00 ++#define PWM_CLOCK_SELECTION 0x04 ++#define PWM_DUTY_CYCLE 0x08 ++#define PWM_M0 0x10 ++#define PWM_M1 0x14 ++#define PWM_N0 0x18 ++#define PWM_N1 0x1c ++#define PWM_CONTROL_EXT 0x40 ++#define PWM_CLOCK_SEL_EXT 0x44 ++#define PWM_O0 0x50 ++#define PWM_O1 0x54 ++#define PWM_CHANNEL_COUNT 8 ++ ++#define PWM_CLK_ENABLE BIT(0) ++#define PWM_DUTY(PCT) (((PCT) * 128) / 100) ++#define PWM_DUTY_VALUE PWM_DUTY(57) ++ ++ ++static inline uint32_t ast_scu_read(uint32_t reg) ++{ ++ uint32_t val = readl(AST_SCU_BASE + reg); ++ ++ debug("ast_scu_read : reg = 0x%08x, val = 0x%08x\n", reg, val); ++ return val; ++} ++ ++static inline void ast_scu_write(uint32_t val, uint32_t reg) ++{ ++ debug("ast_scu_write : reg = 0x%08x, val = 0x%08x\n", reg, val); ++ ++ writel(SCU_PROTECT_UNLOCK, AST_SCU_BASE); ++ writel(val, AST_SCU_BASE + reg); ++#ifdef CONFIG_AST_SCU_LOCK ++ writel(0xaa, AST_SCU_BASE); ++#endif ++} ++ ++static void pwm_init(void) ++{ ++ uint32_t val; ++ uint32_t chan; ++ ++ /* select pwm 0-7 */ ++ val = ast_scu_read(AST_SCU_FUN_PIN_CTRL3); ++ val |= (SCU_FUN_PIN_VPIG7 | SCU_FUN_PIN_VPIG6 | SCU_FUN_PIN_VPIG5 ++ | SCU_FUN_PIN_VPIG4 | SCU_FUN_PIN_VPIG3 | SCU_FUN_PIN_VPIG2 ++ | SCU_FUN_PIN_VPIG1 | SCU_FUN_PIN_VPIG0); ++ ast_scu_write(val, AST_SCU_FUN_PIN_CTRL3); ++ ++ /* disable video output mux */ ++ val = ast_scu_read(AST_SCU_FUN_PIN_CTRL5); ++ val &= 0xffffffcf; ++ ast_scu_write(val, AST_SCU_FUN_PIN_CTRL5); ++ val = readl(AST_SCU_FUN_PIN_CTRL6); ++ val &= 0xfffffffc; ++ ast_scu_write(val, AST_SCU_FUN_PIN_CTRL6); ++ ++ /* SCU reset of PWM module */ ++ val = ast_scu_read(AST_SCU_RESET); ++ val |= SCU_RESET_PWM; ++ ast_scu_write(val, AST_SCU_RESET); ++ val &= ~SCU_RESET_PWM; ++ ast_scu_write(val, AST_SCU_RESET); ++ ++ /* set M, N, and 0 clock regs to 0 */ ++ writel(0, PWM_BASE_ADDR + PWM_M0); ++ writel(0, PWM_BASE_ADDR + PWM_N0); ++ writel(0, PWM_BASE_ADDR + PWM_O0); ++ ++ /* disable fans and tachos, set M type control */ ++ writel(0x1, PWM_BASE_ADDR + PWM_CONTROL); ++ writel(0x1, PWM_BASE_ADDR + PWM_CONTROL_EXT); ++ ++ /* enable pwm channels */ ++ for (chan = 0; chan < PWM_CHANNEL_COUNT; chan++) { ++ uint32_t base = chan < 4 ? PWM_BASE_ADDR : PWM_BASE_ADDR + 0x40; ++ uint8_t ch_duty_shift = 16 * (chan & 0x1); ++ uint8_t ch_pair = (chan & 0x3) / 2; ++ ++ /* enable pwm for the channel */ ++ val = readl(base); ++ val |= ((1 << (chan & 0x3)) << 8); ++ writel(val, base); ++ ++ /* set duty cycle */ ++ val = readl(base + PWM_DUTY_CYCLE + ch_pair * 4); ++ val &= ~(0xffff << ch_duty_shift); ++ val |= (((uint32_t)PWM_DUTY_VALUE) << 8) << ch_duty_shift; ++ writel(val, base + PWM_DUTY_CYCLE + ch_pair * 4); ++ } ++ ++ /* set up clock type M: period = 127 units at 24MHz/8 (resulting ~23kHz period) */ ++ writel(0x7f30, PWM_BASE_ADDR + PWM_CLOCK_SELECTION); ++ ++ /* enable pwm-tacho */ ++ ++ val = readl(PWM_BASE_ADDR + PWM_CONTROL); ++ val |= PWM_CLK_ENABLE; ++ writel(val, PWM_BASE_ADDR + PWM_CONTROL); ++} ++ + extern void espi_init(void); + void ast_g5_intel(void) + { ++ pwm_init(); + gpio_init(gpio_table, ARRAY_SIZE(gpio_table)); + espi_init(); + sgpio_init(); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0014-Keep-interrupts-enabled-until-last-second.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0014-Keep-interrupts-enabled-until-last-second.patch new file mode 100644 index 000000000..91665e064 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0014-Keep-interrupts-enabled-until-last-second.patch @@ -0,0 +1,91 @@ +From 2078771e0ff84be710250b2e9b2e887f7238f9cc Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Sat, 17 Nov 2018 14:17:27 -0800 +Subject: [PATCH 2/3] Keep interrupts enabled until last second + +The U-Boot bootm command disabled interrupts almost first thing. This +would prevent a person hitting the power button on the host immediatly +after AC on because the BMC would fail to respond to the espi interrupts +and the host would power off. + +Change-Id: I6c0fb5cca1be6c326da4c9a3d3dfbab89dac9928 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + board/aspeed/ast-g5/ast-g5-intel.c | 8 ++++++++ + common/bootm.c | 7 ------- + common/bootm_os.c | 1 + + 3 files changed, 9 insertions(+), 7 deletions(-) + +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index f810ded4e7..4d399be392 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -263,6 +263,14 @@ int intel_force_firmware_jumper_enabled(void) + return gpio_get_value(GPIO_FF_UPD_JUMPER); + } + ++void arch_preboot_os(void) ++{ ++ // last second before booting... set the LEDs ++ id_led_control(GPIO_ID_LED, EIDLED_On); ++ id_led_control(GPIO_GREEN_LED, EIDLED_On); ++ id_led_control(GPIO_AMBER_LED, EIDLED_Off); ++} ++ + /* PWM offsets */ + + #define PWM_BASE_ADDR 0x1E786000 +diff --git a/common/bootm.c b/common/bootm.c +index 2431019b3f..46909ecdbb 100644 +--- a/common/bootm.c ++++ b/common/bootm.c +@@ -602,7 +602,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int states, bootm_headers_t *images, int boot_progress) + { + boot_os_fn *boot_fn; +- ulong iflag = 0; + int ret = 0, need_boot_fn; + + images->state |= states; +@@ -626,7 +625,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + if (!ret && (states & BOOTM_STATE_LOADOS)) { + ulong load_end; + +- iflag = bootm_disable_interrupts(); + ret = bootm_load_os(images, &load_end, 0); + if (ret == 0) + lmb_reserve(&images->lmb, images->os.load, +@@ -670,8 +668,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); + if (boot_fn == NULL && need_boot_fn) { +- if (iflag) +- enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); +@@ -711,9 +707,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + + /* Deal with any fallout */ + err: +- if (iflag) +- enable_interrupts(); +- + if (ret == BOOTM_ERR_UNIMPLEMENTED) + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) +diff --git a/common/bootm_os.c b/common/bootm_os.c +index 9ec84bd0db..b56eb39780 100644 +--- a/common/bootm_os.c ++++ b/common/bootm_os.c +@@ -476,6 +476,7 @@ __weak void arch_preboot_os(void) + int boot_selected_os(int argc, char * const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn) + { ++ disable_interrupts(); + arch_preboot_os(); + boot_fn(state, argc, argv, images); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0015-Rewrite-memmove-to-optimize-on-word-transfers.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0015-Rewrite-memmove-to-optimize-on-word-transfers.patch new file mode 100644 index 000000000..475b8c3ff --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0015-Rewrite-memmove-to-optimize-on-word-transfers.patch @@ -0,0 +1,114 @@ +From a71794fc928429e199c5ea48181e5edfbb0c4f39 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Mon, 19 Nov 2018 11:04:02 -0800 +Subject: [PATCH] Rewrite memmove to optimize on word transfers + +Reading from the flash at boot time was using byte-sized transfers, +which ultimately turns into four word transfers over spi for every real +word read. This change breaks memmove down into a header, body, and +trailer, where the body is all done with word-sized transfers. + +Change-Id: Ie0a1f3261e507fb34a908571883d9bf04a1059ee +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + lib/string.c | 77 +++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 64 insertions(+), 13 deletions(-) + +diff --git a/lib/string.c b/lib/string.c +index 67d5f6a421..0bf472f1f6 100644 +--- a/lib/string.c ++++ b/lib/string.c +@@ -505,26 +505,77 @@ void * memcpy(void *dest, const void *src, size_t count) + * + * Unlike memcpy(), memmove() copes with overlapping areas. + */ +-void * memmove(void * dest,const void *src,size_t count) ++void *memmove(void *dest, const void *src, size_t count) + { +- char *tmp, *s; +- +- if (src == dest) ++ unsigned char *bdst = (unsigned char *)dest; ++ const unsigned char *bsrc = (const unsigned char *)src; ++ unsigned long *ldst; ++ const unsigned long *lsrc; ++ size_t unaligned_header = 0, unaligned_trailer = 0; ++ size_t unaligned_src = ++ (sizeof(*ldst) - (size_t)src) & (sizeof(*ldst) - 1); ++ size_t unaligned_dst = ++ (sizeof(*ldst) - (size_t)dest) & (sizeof(*ldst) - 1); ++ ++ if (src == dest || !count) + return dest; + ++ if (unaligned_src || unaligned_dst) { ++ if (unaligned_dst != unaligned_src) { ++ unaligned_header = count; ++ } else { ++ unaligned_header = unaligned_src; ++ if (unaligned_header > count) { ++ unaligned_header = count; ++ } ++ } ++ count -= unaligned_header; ++ } ++ if (count & (sizeof(*ldst) - 1)) { ++ unaligned_trailer = count & (sizeof(*ldst) - 1); ++ count -= unaligned_trailer; ++ } ++ + if (dest <= src) { +- tmp = (char *) dest; +- s = (char *) src; +- while (count--) +- *tmp++ = *s++; ++ /* possible un-aligned bytes */ ++ while (unaligned_header--) ++ *bdst++ = *bsrc++; ++ ++ /* aligned words */ ++ ldst = (unsigned long *)bdst; ++ lsrc = (const unsigned long *)bsrc; ++ while (count >= sizeof(*ldst)) { ++ count -= sizeof(*ldst); ++ *ldst++ = *lsrc++; + } +- else { +- tmp = (char *) dest + count; +- s = (char *) src + count; +- while (count--) +- *--tmp = *--s; ++ ++ /* possibly un-aligned bytes */ ++ bdst = (unsigned char *)ldst; ++ bsrc = (const unsigned char *)lsrc; ++ while (unaligned_trailer--) ++ *bdst++ = *bsrc++; ++ } else { ++ bdst += unaligned_header + count + unaligned_trailer; ++ bsrc += unaligned_header + count + unaligned_trailer; ++ ++ /* possibly un-aligned bytes */ ++ while (unaligned_trailer--) ++ *--bdst = *--bsrc; ++ ++ /* aligned words */ ++ ldst = (unsigned long *)bdst; ++ lsrc = (unsigned long *)bsrc; ++ while (count >= sizeof(*ldst)) { ++ count -= sizeof(*ldst); ++ *--ldst = *--lsrc; + } + ++ /* possibly un-aligned bytes */ ++ bdst = (unsigned char *)ldst; ++ bsrc = (const unsigned char *)lsrc; ++ while (unaligned_header--) ++ *--bdst = *--bsrc; ++ } + return dest; + } + #endif +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch new file mode 100644 index 000000000..cc1e56fdb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch @@ -0,0 +1,28 @@ +From 8992df8f3a0f5fc16ec41ad6dd7a5a13e6f94d32 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 5 Dec 2018 16:13:15 -0800 +Subject: [PATCH] Add support for 128MB Macronix spi flash MX66L1G45G + +This will enable u-boot support for the Macronix MX66L1G45G part. + +Change-Id: I5edc69357a8b1607c5c44e53bed3eddf38fdc0be +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + drivers/mtd/spi/sf_params.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c +index c577d9ed6c..d2a96efe48 100644 +--- a/drivers/mtd/spi/sf_params.c ++++ b/drivers/mtd/spi/sf_params.c +@@ -50,6 +50,7 @@ const struct spi_flash_params spi_flash_params_table[] = { + {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP}, + {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP}, ++ {"MX66L1G45G", 0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL, SECT_4K|WR_QPP}, + {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + #endif + #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0017-Enable-Macronix-and-Micron-SPI-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0017-Enable-Macronix-and-Micron-SPI-support.patch new file mode 100644 index 000000000..b26803c95 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0017-Enable-Macronix-and-Micron-SPI-support.patch @@ -0,0 +1,54 @@ +From 8415002a2e77a41831b3064dae264f60996ac88a Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 6 Dec 2018 15:07:09 -0800 +Subject: [PATCH] Enable Macronix and Micron SPI support + +This commit enables Macronix and Micron SPI support. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + configs/ast_g5_ncsi_2boot_defconfig | 2 ++ + configs/ast_g5_ncsi_defconfig | 2 ++ + configs/ast_g5_phy_defconfig | 2 ++ + 3 files changed, 6 insertions(+) + +diff --git a/configs/ast_g5_ncsi_2boot_defconfig b/configs/ast_g5_ncsi_2boot_defconfig +index d5b7894a9e00..abceed817615 100644 +--- a/configs/ast_g5_ncsi_2boot_defconfig ++++ b/configs/ast_g5_ncsi_2boot_defconfig +@@ -10,6 +10,8 @@ CONFIG_FIT_VERBOSE=y + CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y ++CONFIG_SPI_FLASH_MACRONIX=y ++CONFIG_SPI_FLASH_STMICRO=y + CONFIG_SYS_NS16550=y + CONFIG_FIRMWARE_2ND_BOOT=y + CONFIG_AUTOBOOT=y +diff --git a/configs/ast_g5_ncsi_defconfig b/configs/ast_g5_ncsi_defconfig +index 9481e5fb6e9d..3f504a325649 100644 +--- a/configs/ast_g5_ncsi_defconfig ++++ b/configs/ast_g5_ncsi_defconfig +@@ -10,5 +10,7 @@ CONFIG_FIT_VERBOSE=y + CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y ++CONFIG_SPI_FLASH_MACRONIX=y ++CONFIG_SPI_FLASH_STMICRO=y + CONFIG_SYS_NS16550=y + CONFIG_USE_IRQ=y +diff --git a/configs/ast_g5_phy_defconfig b/configs/ast_g5_phy_defconfig +index 4aefcf49e880..8f0919043376 100644 +--- a/configs/ast_g5_phy_defconfig ++++ b/configs/ast_g5_phy_defconfig +@@ -11,5 +11,7 @@ CONFIG_FIT_VERBOSE=y + CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y ++CONFIG_SPI_FLASH_MACRONIX=y ++CONFIG_SPI_FLASH_STMICRO=y + CONFIG_SYS_NS16550=y + CONFIG_USE_IRQ=y +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch new file mode 100644 index 000000000..b96211118 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch @@ -0,0 +1,65 @@ +From 5f2a7c03c877454eb78a406934abf6b7d05e40a6 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 6 Dec 2018 18:49:04 -0800 +Subject: [PATCH] Add support for Macronix and Micron 1Gbits SPI flash + +Quick fix to support Macronix and Micron 1Gbits SPI. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/mach-aspeed/flash.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/arch/arm/mach-aspeed/flash.c b/arch/arm/mach-aspeed/flash.c +index f9f1345523d4..abc0420176a6 100644 +--- a/arch/arm/mach-aspeed/flash.c ++++ b/arch/arm/mach-aspeed/flash.c +@@ -79,6 +79,8 @@ flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* FLASH chips info */ + #define MX25L12805D 0x1820C2 + #define MX25L25635E 0x1920C2 + #define MX66L51235F 0x1A20C2 ++#define MX66L1G45G 0x1B20C2 ++#define MT25QL01GB 0x21BA20 + #define SST25VF016B 0x4125bf + #define SST25VF064C 0x4b25bf + #define SST25VF040B 0x8d25bf +@@ -977,6 +979,36 @@ static ulong flash_get_size (ulong base, flash_info_t *info) + #endif + break; + ++ case MT25QL01GB: ++ case MX66L1G45G: ++ erase_region_size = 0x10000; ++ info->readcmd = 0x0b; ++ info->dualport = 0; ++ info->dummybyte = 1; ++ info->buffersize = 256; ++ WriteClk = 50; ++ EraseClk = 20; ++ ReadClk = 50; ++#if 1 ++ info->sector_count = 2048; ++ info->size = 0x4000000; ++ info->address32 = 1; ++#if defined(CONFIG_FLASH_SPIx2_Dummy) ++ info->readcmd = 0xbb; ++ info->dummybyte = 1; ++ info->dualport = 1; ++ info->iomode = IOMODEx2_dummy; ++#elif defined(CONFIG_FLASH_SPIx4_Dummy) ++ info->readcmd = 0xeb; ++ info->dummybyte = 3; ++ info->dualport = 0; ++ info->iomode = IOMODEx4_dummy; ++ info->quadport = 1; ++ info->dummydata = 0xaa; ++#endif ++#endif ++ break; ++ + case MX25L12805D: + info->sector_count = 256; + info->size = 0x1000000; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0019-u-boot-full-platform-reset-espi-oob-ready.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0019-u-boot-full-platform-reset-espi-oob-ready.patch new file mode 100644 index 000000000..1d2b02954 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0019-u-boot-full-platform-reset-espi-oob-ready.patch @@ -0,0 +1,48 @@ +From bb490aa226dcf261d3d6865be37130765ecbe9f4 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Mon, 17 Dec 2018 20:37:23 -0800 +Subject: [PATCH] u-boot: full platform reset + espi oob-ready + +If the platform is strapped for fast reset, have platform-g5.S do a full +reset and then immediately set oob-ready so the espi master controller +can initiate communication. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + arch/arm/mach-aspeed/platform_g5.S | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/mach-aspeed/platform_g5.S b/arch/arm/mach-aspeed/platform_g5.S +index 2ac1ca4721..66427b6f33 100644 +--- a/arch/arm/mach-aspeed/platform_g5.S ++++ b/arch/arm/mach-aspeed/platform_g5.S +@@ -139,7 +139,7 @@ + But if FW has other initial code executed before platform.S, then it should use WDT_SOC mode. + Use WDT_Full may clear the initial result of prior initial code. + ******************************************************************************/ +-//#define ASTMMC_INIT_RESET_MODE_FULL ++#define ASTMMC_INIT_RESET_MODE_FULL + + /****************************************************************************** + There is a compatibility issue for Hynix DDR4 SDRAM. +@@ -563,6 +563,17 @@ wait_first_reset: + *******************************************/ + + bypass_first_reset: ++ /* Timing from ESPI master requires OOB channel ready bit be set early */ ++ ldr r0, =0x1e6e2070 @ check strapping for eSPI mode ++ tst r0, #0x02000000 @ Test for bit 25 - eSPI Mode ++ beq espi_early_init_done @ if bit 25 clear, dont set OOB ready ++ ++ ldr r0, =0x1e6ee000 ++ ldr r1, [r0] @ ESPI000: ESPI Engine Control Reg ++ orr r1, r1, #0x00000010 @ Set OOB Channel Ready bit ++ str r1, [r0] ++espi_early_init_done: ++ + /* Enable Timer separate clear mode */ + ldr r0, =0x1e782038 + mov r1, #0xAE +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Add-system-reset-status-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Add-system-reset-status-support.patch new file mode 100644 index 000000000..814a7dc2a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Add-system-reset-status-support.patch @@ -0,0 +1,206 @@ +From d0338f68edb7f818b45ee43765cf124c14ccae03 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Tue, 22 Jan 2019 16:15:36 +0800 +Subject: [PATCH] Add system reset status support + +Will display the reset reasons and other CPU information in u-boot, +and save the reset reasons into kernel command line, +for applications to query. + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + arch/arm/include/asm/arch-aspeed/ast_scu.h | 1 + + arch/arm/include/asm/arch-aspeed/platform.h | 2 ++ + arch/arm/mach-aspeed/ast-scu.c | 55 +++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-intel.c | 30 ++++++++++++++++ + board/aspeed/ast-g5/ast-g5.c | 7 ++++ + 5 files changed, 95 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h +index c10e6a9..369c4e3 100644 +--- a/arch/arm/include/asm/arch-aspeed/ast_scu.h ++++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h +@@ -37,6 +37,7 @@ extern void ast_scu_get_who_init_dram(void); + + extern u32 ast_get_clk_source(void); + extern u32 ast_get_h_pll_clk(void); ++extern u32 ast_get_m_pll_clk(void); + extern u32 ast_get_ahbclk(void); + + extern u32 ast_scu_get_vga_memsize(void); +diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h +index 3b06e52..4e4140d 100644 +--- a/arch/arm/include/asm/arch-aspeed/platform.h ++++ b/arch/arm/include/asm/arch-aspeed/platform.h +@@ -29,6 +29,8 @@ + #include <asm/arch/ast_g5_platform.h> + #include <asm/arch/ast-g5-intel.h> + #define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_early_init_f */ ++#define CONFIG_BOARD_LATE_INIT 1 /* Call board_late_init */ ++#define CONFIG_DISPLAY_CPUINFO 1 + #else + #err "No define for platform.h" + #endif +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index 63e9c7c..f4268f3 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -100,6 +100,7 @@ static struct soc_id soc_map_table[] = { + SOC_ID("AST2510-A1", 0x04010103), + SOC_ID("AST2520-A1", 0x04010203), + SOC_ID("AST2530-A1", 0x04010403), ++ SOC_ID("AST2500-A2", 0x04030303), + }; + + void ast_scu_init_eth(u8 num) +@@ -235,6 +236,29 @@ u32 ast_get_h_pll_clk(void) + return clk; + } + ++u32 ast_get_m_pll_clk(void) ++{ ++ u32 clk=0; ++ u32 m_pll_set = ast_scu_read(AST_SCU_M_PLL); ++ ++ if(m_pll_set & SCU_M_PLL_OFF) ++ return 0; ++ ++ // Programming ++ clk = ast_get_clk_source(); ++ if(m_pll_set & SCU_M_PLL_BYPASS) { ++ return clk; ++ } else { ++ //PD == SCU20[13:18] ++ //M == SCU20[5:12] ++ //N == SCU20[0:4] ++ //mpll = 24MHz * [(M+1) /(N+1)] / (P+1) ++ clk = ((clk * (SCU_M_PLL_GET_MNUM(m_pll_set) + 1)) / (SCU_M_PLL_GET_NNUM(m_pll_set) + 1))/(SCU_M_PLL_GET_PDNUM(m_pll_set) + 1); ++ } ++ debug("m_pll = %d\n",clk); ++ return clk; ++} ++ + u32 ast_get_ahbclk(void) + { + unsigned int axi_div, ahb_div, hpll; +@@ -304,6 +328,33 @@ u32 ast_get_h_pll_clk(void) + return clk; + } + ++u32 ast_get_m_pll_clk(void) ++{ ++ u32 clk=0; ++ u32 m_pll_set = ast_scu_read(AST_SCU_M_PLL); ++ ++ if(m_pll_set & SCU_M_PLL_OFF) ++ return 0; ++ ++ // Programming ++ clk = ast_get_clk_source(); ++ if(m_pll_set & SCU_M_PLL_BYPASS) { ++ return clk; ++ } else { ++ //OD == SCU24[4] ++ //OD = SCU_M_PLL_GET_DIV(h_pll_set); ++ //Numerator == SCU24[10:5] ++ //num = SCU_M_PLL_GET_NUM(h_pll_set); ++ //Denumerator == SCU24[3:0] ++ //denum = SCU_M_PLL_GET_DENUM(h_pll_set); ++ ++ //hpll = 24MHz * (2-OD) * ((Numerator+2)/(Denumerator+1)) ++ clk = (clk * (2-SCU_M_PLL_GET_DIV(m_pll_set)) * ((SCU_M_PLL_GET_NUM(m_pll_set)+2)/(SCU_M_PLL_GET_DENUM(m_pll_set)+1))); ++ } ++ debug("m_pll = %d\n",clk); ++ return clk; ++} ++ + u32 ast_get_ahbclk(void) + { + unsigned int div, hpll; +@@ -452,6 +503,9 @@ void ast_scu_sys_rest_info(void) + { + u32 rest = ast_scu_read(AST_SCU_SYS_CTRL); + ++#ifdef AST_SOC_G5 ++ printf("RST : 0x%02x\n", rest); ++#else + if (rest & SCU_SYS_EXT_RESET_FLAG) { + printf("RST : External\n"); + ast_scu_write(SCU_SYS_EXT_RESET_FLAG, AST_SCU_SYS_CTRL); +@@ -464,6 +518,7 @@ void ast_scu_sys_rest_info(void) + } else { + printf("RST : CLK en\n"); + } ++#endif + } + + u32 ast_scu_get_vga_memsize(void) +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index bcaf81e..1e8708a 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -303,6 +303,36 @@ static inline void ast_scu_write(uint32_t val, uint32_t reg) + #endif + } + ++void ast_g5_intel_late_init(void) ++{ ++ char *cmdline = NULL; ++ char *cmdline_new = NULL; ++ char buf[32]; ++ u32 rest = 0; ++ ++ /* save and clear reset status */ ++ rest = ast_scu_read(AST_SCU_SYS_CTRL); ++ snprintf(buf, sizeof(buf), " resetreason=0x%x", rest); ++ ast_scu_write(0, AST_SCU_SYS_CTRL); ++ ++ cmdline = getenv("bootargs"); ++ if (!cmdline) { ++ printf("Get bootargs fail!\n"); ++ return; ++ } ++ ++ cmdline_new = malloc(strlen(cmdline) + strlen(buf) + 1); ++ if (!cmdline_new) { ++ printf("Cannot malloc memory!\n"); ++ return; ++ } ++ ++ /* append the reset status into kernel command line */ ++ snprintf(cmdline_new, strlen(cmdline) + strlen(buf) + 1, "%s%s", cmdline, buf); ++ setenv("bootargs", cmdline_new); ++ free(cmdline_new); ++} ++ + static void pwm_init(void) + { + uint32_t val; +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index d41ef9c..0953677 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -19,6 +19,7 @@ + DECLARE_GLOBAL_DATA_PTR; + + extern void ast_g5_intel(void); ++extern void ast_g5_intel_late_init(void); + + int board_early_init_f(void) + { +@@ -40,6 +41,12 @@ int board_init(void) + return 0; + } + ++int board_late_init(void) ++{ ++ ast_g5_intel_late_init(); ++ return 0; ++} ++ + int dram_init(void) + { + u32 vga = ast_scu_get_vga_memsize(); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Enable-PCIe-L1-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Enable-PCIe-L1-support.patch new file mode 100644 index 000000000..bdf3d2ddd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Enable-PCIe-L1-support.patch @@ -0,0 +1,38 @@ +From 22c61ba094d8ebdbdcb44f848eef1f5d87a4be87 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 8 Jan 2019 13:33:15 -0800 +Subject: [PATCH] Enable PCIe L1 support + +This commit enables PCIe L1 support using magic registers. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/mach-aspeed/platform_g5.S | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/arm/mach-aspeed/platform_g5.S b/arch/arm/mach-aspeed/platform_g5.S +index 66427b6f33e7..b4043534b083 100644 +--- a/arch/arm/mach-aspeed/platform_g5.S ++++ b/arch/arm/mach-aspeed/platform_g5.S +@@ -2432,6 +2432,18 @@ spi_cbr_end: + bic r1, r1, #0x00400000 + str r1, [r0] + ++ ldr r0, =0x1e6ed07c @ Enable PCIe L1 support ++ ldr r1, =0xa8 ++ str r1, [r0] ++ ++ ldr r0, =0x1e6ed068 ++ ldr r1, =0xc81f0a ++ str r1, [r0] ++ ++ ldr r0, =0x1e6ed07c ++ mov r1, #0 ++ str r1, [r0] ++ + /****************************************************************************** + Configure MAC timing + ******************************************************************************/ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0021-Config-host-uart-clock-source-using-environment-vari.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0021-Config-host-uart-clock-source-using-environment-vari.patch new file mode 100755 index 000000000..310d9359b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0021-Config-host-uart-clock-source-using-environment-vari.patch @@ -0,0 +1,108 @@ +From c82ba33ea40e0007945cbc93da58f296fdeedeaf Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 11 Feb 2019 15:19:56 +0800 +Subject: [PATCH] Config host uart clock source using environment variable + +In order to support high speed uart for host uarts, +the uart clock needs to be switched between 24M and 192M. +Config SCU4C based on environment variable hostserialcfg, +this variable is set by IPMI OEM commands. + +Tested: +Change the hostserialcfg variable to 0/1/2/3, +using the below commands: + +ipmitool raw 0x32 0x90 1 0; reboot +cat /sys/class/tty/ttyS*/uartclk, all should be 24MHz + +ipmitool raw 0x32 0x90 1 1; reboot +cat /sys/class/tty/ttyS*/uartclk, ttyS0/2/3 should be 192MHz + +ipmitool raw 0x32 0x90 1 2; reboot +cat /sys/class/tty/ttyS*/uartclk, ttyS1 should be 192MHz + +ipmitool raw 0x32 0x90 1 3; reboot +cat /sys/class/tty/ttyS*/uartclk, ttyS0/12/3 should be 192MHz + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + arch/arm/include/asm/arch-aspeed/regs-scu.h | 5 ++++ + board/aspeed/ast-g5/ast-g5-intel.c | 39 +++++++++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/regs-scu.h b/arch/arm/include/asm/arch-aspeed/regs-scu.h +index 10b983a..8a596ce 100644 +--- a/arch/arm/include/asm/arch-aspeed/regs-scu.h ++++ b/arch/arm/include/asm/arch-aspeed/regs-scu.h +@@ -529,6 +529,11 @@ + /* AST_SCU_MAC_CLK 0x48 - MAC interface clock delay setting register */ + + /* AST_SCU_MISC2_CTRL 0x4C - Misc. 2 Control register */ ++#define SCU_UART5_HS_CLOCK (1 << 28) ++#define SCU_UART4_HS_CLOCK (1 << 27) ++#define SCU_UART3_HS_CLOCK (1 << 26) ++#define SCU_UART2_HS_CLOCK (1 << 25) ++#define SCU_UART1_HS_CLOCK (1 << 24) + #ifdef AST_SOC_G5 + #define SCU_PCIE_MAPPING_HIGH (1 << 15) + #define SCU_MALI_DTY_MODE (1 << 8) +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 1e8708a..f810a40 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -103,6 +103,9 @@ static const GPIOValue gpio_table[] = { + #define SGPIO_ENABLE 1 + #define GPIO254 0x254 + ++#define HOST_SERIAL_A_HIGH_SPEED (1 << 0) ++#define HOST_SERIAL_B_HIGH_SPEED (1 << 1) ++ + static void sgpio_init(void) + { + uint32_t value; +@@ -310,6 +313,42 @@ void ast_g5_intel_late_init(void) + char buf[32]; + u32 rest = 0; + ++ /* By default host serail A and B use normal speed */ ++ uint32_t host_serial_cfg = 0; ++ char *host_serial_cfg_txt = NULL; ++ ++ /* Config the uart clock source based on environment configuration */ ++ host_serial_cfg_txt = getenv("hostserialcfg"); ++ ++ if (host_serial_cfg_txt != NULL) ++ host_serial_cfg = simple_strtoul(host_serial_cfg_txt, NULL, 16); ++ ++ if (host_serial_cfg > (HOST_SERIAL_A_HIGH_SPEED | HOST_SERIAL_B_HIGH_SPEED)) { ++ printf("Invalided hostserialcfg %x, use default!\n", host_serial_cfg); ++ host_serial_cfg = 0; ++ } ++ ++ /* SOL implementation requires uart1/uart3/uart4 have the same clock ++ * source for data forwarding, config uart3 and uart4 ++ */ ++ if (host_serial_cfg & HOST_SERIAL_A_HIGH_SPEED) { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) | ++ SCU_UART1_HS_CLOCK | SCU_UART3_HS_CLOCK | ++ SCU_UART4_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } else { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & ++ ~SCU_UART1_HS_CLOCK & ~SCU_UART3_HS_CLOCK & ++ ~SCU_UART4_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } ++ ++ if (host_serial_cfg & HOST_SERIAL_B_HIGH_SPEED) { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) | ++ SCU_UART2_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } else { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & ++ ~SCU_UART2_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } ++ + /* save and clear reset status */ + rest = ast_scu_read(AST_SCU_SYS_CTRL); + snprintf(buf, sizeof(buf), " resetreason=0x%x", rest); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend new file mode 100644 index 000000000..9494c823e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend @@ -0,0 +1,28 @@ +FILESEXTRAPATHS_append_wolfpass:= "${THISDIR}/files:" + +# the meta-phosphor layer adds this patch, which conflicts +# with the intel layout for environment +SRC_URI_remove_wolfpass = " file://0001-configs-ast-Add-redundnant-env.patch" + +SRC_URI_append_wolfpass = " file://0001-flash-use-readX-writeX-not-udelay.patch \ + file://0002-intel-layout-environment-addr.patch \ + file://0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch \ + file://0005-enable-passthrough-in-uboot.patch \ + file://0006-Add-Aspeed-g5-interrupt-support.patch \ + file://0007-Add-espi-support.patch \ + file://0008-add-sgio-support-for-port80-snoop-post-LEDs.patch \ + file://0009-Add-basic-GPIO-support.patch \ + file://0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch \ + file://0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch \ + file://0012-Add-status-and-ID-LED-support.patch \ + file://0013-aspeed-Add-Pwm-Driver.patch \ + file://0014-Keep-interrupts-enabled-until-last-second.patch \ + file://0015-Rewrite-memmove-to-optimize-on-word-transfers.patch \ + file://0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch \ + file://0017-Enable-Macronix-and-Micron-SPI-support.patch \ + file://0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch \ + file://0019-u-boot-full-platform-reset-espi-oob-ready.patch \ + file://0020-Enable-PCIe-L1-support.patch \ + file://0020-Add-system-reset-status-support.patch \ + file://0021-Config-host-uart-clock-source-using-environment-vari.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/at-scale-debug.bb b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/at-scale-debug.bb new file mode 100644 index 000000000..0cf8ed0f0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/at-scale-debug.bb @@ -0,0 +1,24 @@ +inherit obmc-phosphor-systemd + +SUMMARY = "At Scale Debug Service" +DESCRIPTION = "At Scale Debug Service exposes remote JTAG target debug capabilities" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=40c94c59cbbc218afdd64eec899ad2f6" + +inherit cmake +DEPENDS = "sdbusplus openssl libpam" + +do_configure[depends] += "virtual/kernel:do_shared_workdir" + +SRC_URI = "git://git@github.com/Intel-BMC/at-scale-debug;protocol=ssh" + +SRCREV = "e3ef64c6427f7be7c9cd6aa4cd696dd5c33f5085" +S = "${WORKDIR}/git" + +SYSTEMD_SERVICE_${PN} += "com.intel.AtScaleDebug.service" + +# Specify any options you want to pass to cmake using EXTRA_OECMAKE: +EXTRA_OECMAKE = "-DBUILD_UT=OFF" + +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/" diff --git a/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebug.service b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebug.service new file mode 100644 index 000000000..65d83b4c4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebug.service @@ -0,0 +1,13 @@ +[Unit] +Description=Intel BMC At Scale Debug +Requires=com.intel.AtScaleDebugJtagTest.service network-online.target + +[Service] +Restart=always +RestartSec=30 +ExecStart={bindir}/asd -k /home/root/server.pem +Type=simple +SyslogIdentifier=asd + +[Install] +WantedBy=obmc-host-start@0.target diff --git a/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebugJtagTest.service b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebugJtagTest.service new file mode 100644 index 000000000..281d1a993 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebugJtagTest.service @@ -0,0 +1,12 @@ +[Unit] +Description=Intel BMC At Scale Debug JTAG test to check if remote debug setting is enabled + +[Service] +Type=oneshot +ExecStartPre=/bin/sleep 10 +ExecStart={bindir}/jtag_test +SyslogIdentifier=jtag_test +RemainAfterExit=true + +[Install] +WantedBy=obmc-host-start@0.target diff --git a/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files/fstab b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files/fstab new file mode 100644 index 000000000..0b53093ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files/fstab @@ -0,0 +1,10 @@ +/dev/root / auto defaults 1 1 +proc /proc proc defaults 0 0 +devpts /dev/pts devpts mode=0620,gid=5 0 0 +tmpfs /run tmpfs mode=0755,nodev,nosuid,strictatime 0 0 +tmpfs /var/lib/systemd/coredump tmpfs rw,nosuid,nodev,size=67108864 0 0 +tmpfs /media tmpfs rw 0 0 + +# uncomment this if your device has a SD/MMC/Transflash slot +#/dev/mmcblk0p1 /media/card auto defaults,sync,noauto 0 0 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files_%.bbappend new file mode 100644 index 000000000..79e529179 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files_%.bbappend @@ -0,0 +1,2 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI_append = " file://fstab" diff --git a/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/cpu-log-util_git.bb b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/cpu-log-util_git.bb new file mode 100644 index 000000000..170412e0c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/cpu-log-util_git.bb @@ -0,0 +1,27 @@ +inherit obmc-phosphor-dbus-service +inherit obmc-phosphor-systemd + +SUMMARY = "CPU Log Utils" +DESCRIPTION = "CPU utilities for dumping CPU registers over PECI" + +DEPENDS = "boost cjson sdbusplus " +inherit cmake + +LICENSE = "CLOSED" +LIC_FILES_CHKSUM = "" + +SRC_URI = "git://git@github.com/Intel-BMC/at-scale-debug;protocol=ssh" +SRCREV = "e3ef64c6427f7be7c9cd6aa4cd696dd5c33f5085" + +S = "${WORKDIR}/git/cpu-log-util" +PACKAGES += "libpeci" + +SYSTEMD_SERVICE_${PN} += "com.intel.CpuDebugLog.service" +DBUS_SERVICE_${PN} += "com.intel.CpuDebugLog.service" + +# linux-libc-headers guides this way to include custom uapi headers +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/files/com.intel.CpuDebugLog.service b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/files/com.intel.CpuDebugLog.service new file mode 100644 index 000000000..13d2c860e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/files/com.intel.CpuDebugLog.service @@ -0,0 +1,10 @@ +[Unit] +Description=Intel BMC CPU Debug Log + +[Service] +Restart=always +ExecStart={bindir}/cpu_log +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh new file mode 100644 index 000000000..5a2637ba3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +SSH_ID=$HOME/.ssh/id_rsa.db +[ -e $HOME/.fwupd.defaults ] && source $HOME/.fwupd.defaults + +usage() { + echo "usage: $(basename $0) uri" + echo " uri is something like: file:///path/to/fw" + echo " tftp://tftp.server.ip.addr/path/to/fw" + echo " scp://[user@]scp.server.ip.addr:/path/to/fw" + echo " http[s]://web.server.ip.addr/path/to/fw" + echo " ftp://[user@]ftp.server.ip.addr/path/to/fw" + exit 1 +} + +if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then usage; fi +if [ $# -eq 0 ]; then + # set DEFURI in $HOME/.fwupd.defaults + URI="$DEFURI" +else + URI="$1" +fi + +PROTO=$(echo "$URI" | sed 's,\([a-z]*\)://.*$,\1,') +REMOTE=$(echo "$URI" | sed 's,.*://\(.*\)$,\1,') +REMOTE_HOST=$(echo "$REMOTE" | sed 's,\([^/]*\)/.*$,\1,') +if [ "$PROTO" = 'scp' ]; then + REMOTE_PATH=$(echo "$REMOTE" | cut -d':' -f2) +else + REMOTE_PATH=$(echo "$REMOTE" | sed 's,[^/]*/\(.*\)$,\1,') +fi +LOCAL_PATH="/tmp/$(basename $REMOTE_PATH)" +echo "PROTO=$PROTO" +echo "REMOTE=$REMOTE" +echo "REMOTE_HOST=$REMOTE_HOST" +echo "REMOTE_PATH=$REMOTE_PATH" +if [ ! -e $LOCAL_PATH ] || [ $(stat -c %s $LOCAL_PATH) -eq 0 ]; then + echo "Download '$REMOTE_PATH' from $PROTO $REMOTE_HOST $REMOTE_PATH" + case "$PROTO" in + scp) + mkdir -p $HOME/.ssh + if [ -e "$SSH_ID" ]; then + ARG_ID="-i $SSH_ID" + fi + scp $ARG_ID $REMOTE_HOST$REMOTE_PATH $LOCAL_PATH + if [ $? -ne 0 ]; then + echo "scp $REMOTE $LOCAL_PATH failed!" + exit 255 + fi + ;; + tftp) + cd /tmp + tftp -g -r "$REMOTE_PATH" "$REMOTE_HOST" + if [ $? -ne 0 ]; then + echo "tftp -g -r \"$REMOTE_PATH\" \"$REMOTE_HOST\" failed!" + exit 255 + fi + ;; + http|https|ftp) + wget --no-check-certificate "$URI" -O "$LOCAL_PATH" + if [ $? -ne 0 ]; then + echo "wget $URI failed!" + exit 255 + fi + ;; + file) + cp "$REMOTE_PATH" "$LOCAL_PATH" + ;; + *) + echo "Invalid URI $URI" + exit 1; + ;; + esac +fi + +# do a quick sanity check on the image +if [ $(stat -c "%s" "$LOCAL_PATH") -lt 10000000 ]; then + echo "Update file "$LOCAL_PATH" seems to be too small" + exit 1 +fi +dtc -I dtb -O dtb "$LOCAL_PATH" > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Update file $LOCAL_PATH doesn't seem to be in the proper format" + exit 1 +fi + +# guess based on fw_env which partition we booted from +BOOTADDR=$(fw_printenv bootcmd | awk '{print $2}') + +TGT="/dev/mtd/image-a" +case "$BOOTADDR" in + 20080000) TGT="/dev/mtd/image-b"; BOOTADDR="22480000" ;; + 22480000) TGT="/dev/mtd/image-a"; BOOTADDR="20080000" ;; + *) TGT="/dev/mtd/image-a"; BOOTADDR="20080000" ;; +esac +echo "Updating $(basename $TGT) (use bootm $BOOTADDR)" +flash_erase $TGT 0 0 +echo "Writing $(stat -c "%s" "$LOCAL_PATH") bytes" +cat "$LOCAL_PATH" > "$TGT" +fw_setenv "bootcmd" "bootm ${BOOTADDR}" + +# reboot +reboot + diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/usb-ctrl b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/usb-ctrl new file mode 100644 index 000000000..ae9f54263 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/usb-ctrl @@ -0,0 +1,136 @@ +#!/bin/sh + +setup_image() +{ + set -x + local storage="$1" + local sz_mb="$2" + # create the backing store + dd if=/dev/zero of=$storage bs=1M seek=$sz_mb count=0 2>/dev/null + # this shows up as 23FC-F676 in /dev/disk/by-uuid + local diskid=0x23FCF676 + mkdosfs -n 'OPENBMC-FW' -i $diskid -I $storage >/dev/null 2>&1 +} + +mount_image() +{ + set -x + local storage="$1" + local stormnt="$2" + mkdir -p $stormnt || exit 1 + mount -o loop -t vfat $storage $stormnt +} + +cleanup_image() +{ + set -x + local storage="$1" + local stormnt="$2" + umount -f "$stormnt" + rm -f "$storage" + rmdir "$stormnt" +} + +GADGET_BASE=/sys/kernel/config/usb_gadget + +which_dev() +{ + local in_use=$(cat $GADGET_BASE/*/UDC) + cd /sys/class/udc + for D in *; do + case "$in_use" in + *"$D"*) ;; + *) echo "$D"; return 0;; + esac + done + return 1 +} + +usb_ms_insert() +{ + local name="$1" + local storage="$2" + + if [ -d $GADGET_BASE/$name ]; then + echo "device $name already exists" >&2 + return 1 + fi + mkdir $GADGET_BASE/$name + cd $GADGET_BASE/$name + + echo 0x1d6b > idVendor # Linux Foundation + echo 0x0105 > idProduct # FunctionFS Gadget + mkdir strings/0x409 + local machineid=$(cat /etc/machine-id) + local data="OpenBMC USB mass storage gadget device serial number" + local serial=$( echo -n "${machineid}${data}${machineid}" | \ + sha256sum | cut -b 0-12 ) + echo $serial > strings/0x409/serialnumber + echo OpenBMC > strings/0x409/manufacturer + echo "OpenBMC Mass Storage" > strings/0x409/product + + mkdir configs/c.1 + mkdir functions/mass_storage.$name + echo $storage > functions/mass_storage.$name/lun.0/file + echo 0 > functions/mass_storage.$name/lun.0/removable + mkdir configs/c.1/strings/0x409 + + echo "Conf 1" > configs/c.1/strings/0x409/configuration + echo 120 > configs/c.1/MaxPower + ln -s functions/mass_storage.$name configs/c.1 + local dev=$(which_dev) + echo $dev > UDC +} + +usb_ms_eject() +{ + local name="$1" + + echo '' > $GADGET_BASE/$name/UDC + + rm -f $GADGET_BASE/$name/configs/c.1/mass_storage.$name + rmdir $GADGET_BASE/$name/configs/c.1/strings/0x409 + rmdir $GADGET_BASE/$name/configs/c.1 + rmdir $GADGET_BASE/$name/functions/mass_storage.$name + rmdir $GADGET_BASE/$name/strings/0x409 + rmdir $GADGET_BASE/$name +} + +usage() +{ + echo "Usage: $0 <action> ..." + echo " $0 setup <file> <sizeMB>" + echo " $0 insert <name> <file>" + echo " $0 eject <name>" + echo " $0 mount <file> <mnt>" + echo " $0 cleanup <file> <mnt>" + exit 1 +} + +echo "$#: $0 $@" +case "$1" in + insert) + shift + usb_ms_insert "$@" + ;; + eject) + shift + usb_ms_eject "$@" + ;; + setup) + shift + setup_image "$@" + ;; + mount) + shift + mount_image "$@" + ;; + cleanup) + shift + cleanup_image "$@" + ;; + *) + usage + ;; +esac +exit $? diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb b/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb new file mode 100644 index 000000000..ac8e58c03 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb @@ -0,0 +1,22 @@ +SUMMARY = "Temporary intel-fw-update script" +DESCRIPTION = "At runtime, perform a firmware update and reboot" +PR = "r1" + +# flash_eraseall +RDEPENDS_intel-fw-update += "mtd-utils" +# wget tftp scp +RDEPENDS_intel-fw-update += "busybox dropbear" +# mkfs.vfat, parted +RDEPENDS_intel-fw-update += "dosfstools parted" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI += "file://fwupd.sh" +SRC_URI += "file://usb-ctrl" + +do_install() { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/fwupd.sh ${D}${bindir} + install -m 0755 ${WORKDIR}/usb-ctrl ${D}${bindir} +} diff --git a/meta-openbmc-mods/meta-common/recipes-core/ipmi/intel-ipmi-oem_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/ipmi/intel-ipmi-oem_%.bbappend new file mode 100644 index 000000000..e0f4d5d2b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/ipmi/intel-ipmi-oem_%.bbappend @@ -0,0 +1,2 @@ +SRC_URI = "git://github.com/openbmc/intel-ipmi-oem.git" +SRCREV = "7944c3072287a3833a29283aafa8650a31cd4ce7" diff --git a/meta-openbmc-mods/meta-common/recipes-core/ipmi/ipmi-providers.bb b/meta-openbmc-mods/meta-common/recipes-core/ipmi/ipmi-providers.bb new file mode 100644 index 000000000..c23da6df2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/ipmi/ipmi-providers.bb @@ -0,0 +1,37 @@ +SUMMARY = "Intel IPMI Providers" +DESCRIPTION = "IPMI Provider Libraries" + +SRC_URI = "git://git@github.com/Intel-BMC/intel-ipmi-providers;protocol=ssh" +SRCREV = "bbb45687fc2993980ac95117d8503ee6c2358965" + +S = "${WORKDIR}/git" +PV = "0.1+git${SRCPV}" + +DEPENDS = "boost phosphor-ipmi-host intel-ipmi-oem systemd microsoft-gsl" + +inherit cmake obmc-phosphor-ipmiprovider-symlink + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2ee41112a44fe7014dce33e26468ba93" + +EXTRA_OECMAKE="-DENABLE_TEST=0 -DYOCTO=1" + +LIBRARY_NAMES += "libmtmcmds.so" +LIBRARY_NAMES += "libsmbioshandler.so" +LIBRARY_NAMES += "libzbridgecmd.so" +LIBRARY_NAMES += "libsmbiosmdrv2.so" +LIBRARY_NAMES += "libfwupdcmds.so" + +HOSTIPMI_PROVIDER_LIBRARY += "${LIBRARY_NAMES}" +NETIPMI_PROVIDER_LIBRARY += "${LIBRARY_NAMES}" + +FILES_${PN}_append = " ${libdir}/ipmid-providers/lib*${SOLIBS}" +FILES_${PN}_append = " ${libdir}/host-ipmid/lib*${SOLIBS}" +FILES_${PN}_append = " ${libdir}/net-ipmid/lib*${SOLIBS}" +FILES_${PN}-dev_append = " ${libdir}/ipmid-providers/lib*${SOLIBSDEV}" + +do_configure_prepend() { + cp -r ${WORKDIR}/recipe-sysroot${libdir}/phosphor-ipmi-host ${S} + cp -r ${WORKDIR}/recipe-sysroot${includedir}/phosphor-ipmi-host ${S} + cp -r ${WORKDIR}/recipe-sysroot${includedir}/intel-ipmi-oem ${S} +} diff --git a/meta-openbmc-mods/meta-common/recipes-core/microsoft-gsl/microsoft-gsl.bb b/meta-openbmc-mods/meta-common/recipes-core/microsoft-gsl/microsoft-gsl.bb new file mode 100644 index 000000000..8cb593bce --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/microsoft-gsl/microsoft-gsl.bb @@ -0,0 +1,24 @@ +# Add GSL: Guideline Support Library for c++ +# https://github.com/Microsoft/GSL + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://LICENSE;md5=363055e71e77071107ba2bb9a54bd9a7" + +SRC_URI = "git://github.com/Microsoft/GSL.git;protocol=https" + +# Modify these as desired +PV = "1.0+git${SRCPV}" +#SRCREV = "${AUTOREV}" +SRCREV = "be43c79742dc36ee55b21c5d531a5ff301d0ef8d" + +S = "${WORKDIR}/git" + +do_install () { + install -d ${D}/usr/include + install -d ${D}/usr/include/gsl + for F in ${S}/include/gsl/*; do + install -m 0644 ${F} ${D}/usr/include/gsl + done +} + +ALLOW_EMPTY_${PN} = "1" diff --git a/meta-openbmc-mods/meta-common/recipes-core/os-release/os-release.bbappend b/meta-openbmc-mods/meta-common/recipes-core/os-release/os-release.bbappend new file mode 100644 index 000000000..584d3b645 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/os-release/os-release.bbappend @@ -0,0 +1,85 @@ +# WARNING! +# +# These modifications to os-release disable the bitbake parse +# cache (for the os-release recipe only). Before copying +# and pasting into another recipe ensure it is understood +# what that means! + +def irun_git(d, oeroot, git_cmd, **kwargs): + err = None + try: + cmd = 'git --work-tree {} --git-dir {}/.git {}'.format(oeroot, oeroot, git_cmd) + ret, err = bb.process.run(cmd, **kwargs) + if err is not None: + ret += err + except bb.process.ExecutionError as e: + ret = '' + if e.stdout is not None: + ret += e.stdout + if e.stderr is not None: + ret += e.stderr + except Exception as e: + ret = str(e) + return ret.strip('\n') + +def repo_status(d, f, repo, tagargs): + import subprocess + + cmd_list = [['HEAD', 'rev-parse HEAD'], + ['TAG', 'describe {} --dirty --long'.format(tagargs)], + ['STATUS', 'status -sb']] + + f.write(('\n# REPOSITORY: {} '.format(os.path.basename(repo))).ljust(80, '+') + '\n') + for item in cmd_list: + f.write('# {}: '.format(item[0])) + sb = irun_git(d, repo, item[1]) + if sb: + sb_lines = sb.split('\n') + if len(sb_lines) == 1: + f.write(sb_lines[0]) + else: + f.write('\n# ' + '\n# '.join(sb_lines)) + f.write('\n') + +python() { + corebase = d.getVar('COREBASE', True) + mibase = os.path.join(corebase, 'meta-openbmc-mods') + obmc_vers = irun_git(d, corebase, 'describe --dirty --long') + meta_vers = irun_git(d, mibase, 'rev-parse HEAD')[0:7] + version_id = '{}-{}'.format(obmc_vers, meta_vers) + if version_id: + d.setVar('VERSION_ID', version_id) + versionList = version_id.split('-') + version = '{}-{}'.format(versionList[0], versionList[1]) + d.setVar('VERSION', version) + + build_id = irun_git(d, corebase, 'describe --abbrev=0') + if build_id: + d.setVar('BUILD_ID', build_id) +} + +OS_RELEASE_FIELDS_append = " BUILD_ID" + +python do_compile_append () { + import glob + with open(d.expand('${B}/os-release'), 'a') as f: + corebase = d.getVar('COREBASE', True) + f.write('\n# Build Configuration Details\n') + repo_status(d, f, corebase, '') + repo_status(d, f, os.path.join(corebase, 'meta-openbmc-mods'), '--tags') + appends_dir = os.path.join(d.getVar('TOPDIR', True), 'workspace', 'appends') + + for fn in glob.glob(os.path.join(appends_dir, '*.bbappend')): + with open(fn, 'r') as bb_f: + for line in bb_f: + if line.startswith('# srctreebase: '): + srctreebase = line.split(':', 1)[1].strip() + repo_status(d, f, srctreebase, '--tags') +} + + +# Ensure the git commands run every time bitbake is invoked. +BB_DONT_CACHE = "1" + +# Make os-release available to other recipes. +SYSROOT_DIRS_append = " ${sysconfdir}" diff --git a/meta-openbmc-mods/meta-common/recipes-core/packagegroups/packagegroup-core-standalone-sdk-target.bbappend b/meta-openbmc-mods/meta-common/recipes-core/packagegroups/packagegroup-core-standalone-sdk-target.bbappend new file mode 100644 index 000000000..87a4c8503 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/packagegroups/packagegroup-core-standalone-sdk-target.bbappend @@ -0,0 +1 @@ +RRECOMMENDS_${PN}_append = " vim cmake sdbusplus" diff --git a/meta-openbmc-mods/meta-common/recipes-core/peci-pcie/peci-pcie_git.bb b/meta-openbmc-mods/meta-common/recipes-core/peci-pcie/peci-pcie_git.bb new file mode 100644 index 000000000..dec71e0d6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/peci-pcie/peci-pcie_git.bb @@ -0,0 +1,24 @@ +# NOTE: LICENSE is being set to "CLOSED" for now. The PCIe reads over PECI expose +# more information than is accessible from the BIOS or OS, so we need to keep this +# internal to Intel until it's resolved. +LICENSE = "CLOSED" +LIC_FILES_CHKSUM = "" +inherit cmake systemd + +SRC_URI = "git://git@github.com/Intel-BMC/provingground;protocol=ssh" + +DEPENDS = "boost sdbusplus cpu-log-util" + +PV = "0.1+git${SRCPV}" +SRCREV = "f4d4bfc3296cb27feb17aa5d1d93b3061b56ce10" + +S = "${WORKDIR}/git/peci_pcie" + +SYSTEMD_SERVICE_${PN} += "xyz.openbmc_project.PCIe.service" + +# linux-libc-headers guides this way to include custom uapi headers +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-core/readline/readline/inputrc b/meta-openbmc-mods/meta-common/recipes-core/readline/readline/inputrc new file mode 100644 index 000000000..7b84c9916 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/readline/readline/inputrc @@ -0,0 +1,61 @@ +# /etc/inputrc - global inputrc for libreadline +# See readline(3readline) and `info rluserman' for more information. + +# Be 8 bit clean. +set input-meta on +set output-meta on + +# To allow the use of 8bit-characters like the german umlauts, comment out +# the line below. However this makes the meta key not work as a meta key, +# which is annoying to those which don't need to type in 8-bit characters. + +# set convert-meta off + +# try to enable the application keypad when it is called. Some systems +# need this to enable the arrow keys. +# set enable-keypad on + +# see /usr/share/doc/bash/inputrc.arrows for other codes of arrow keys + +# do not bell on tab-completion +# set bell-style none + +# some defaults / modifications for the emacs mode +$if mode=emacs + +# allow the use of the Home/End keys + "\e[1~": beginning-of-line + "\e[4~": end-of-line + +# allow the use of the Delete/Insert keys + "\e[3~": delete-char +# "\e[2~": quoted-insert + +# mappings for "page up" and "page down" to step to the beginning/end +# of the history +# "\e[5~": beginning-of-history +# "\e[6~": end-of-history + +# alternate mappings for "page up" and "page down" to search the history +# "\e[5~": history-search-backward +# "\e[6~": history-search-forward + +# # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving +# "\e[5C": forward-word +# "\e[5D": backward-word +# "\e\e[C": forward-word +# "\e\e[D": backward-word + +# $if term=rxvt +# "\e[8~": end-of-line +# $endif + +# for non RH/Debian xterm, can't hurt for RH/DEbian xterm +# "\eOH": beginning-of-line +# "\eOF": end-of-line + +# for freebsd console +# "\e[H": beginning-of-line +# "\e[F": end-of-line + +$endif diff --git a/meta-openbmc-mods/meta-common/recipes-core/readline/readline_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/readline/readline_%.bbappend new file mode 100644 index 000000000..c63a45dd4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/readline/readline_%.bbappend @@ -0,0 +1,2 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI_append = " file://inputrc" diff --git a/meta-openbmc-mods/meta-common/recipes-core/rest-dbus/rest-dbus-static.bb b/meta-openbmc-mods/meta-common/recipes-core/rest-dbus/rest-dbus-static.bb new file mode 100644 index 000000000..429d5b4b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/rest-dbus/rest-dbus-static.bb @@ -0,0 +1,23 @@ +SUMMARY = "Phosphor OpenBMC REST framework" +DESCRIPTION = "Phosphor OpenBMC REST to DBUS daemon." +HOMEPAGE = "http://github.com/openbmc/rest-dbus" +PR = "r1" + +inherit allarch +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI += "git://github.com/openbmc/rest-dbus.git" + +SRCREV = "9273a302e8f2b3c3e939dff77758e90f163bf6a1" + +S = "${WORKDIR}/git" + +FILES_${PN} += "${datadir}/www/rest-dbus/*" + +do_install () { + install -d ${D}${datadir}/www/rest-dbus/res + install -m 644 ${S}/resources/** ${D}${datadir}/www/rest-dbus/res + install -m 644 ${S}/resources/index.html ${D}${datadir}/www/rest-dbus/index.html +} + diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/obmc-targets.bbappend b/meta-openbmc-mods/meta-common/recipes-core/systemd/obmc-targets.bbappend new file mode 100644 index 000000000..3fe1c3f38 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/obmc-targets.bbappend @@ -0,0 +1,9 @@ +# Remove these files since they are provided by obmc-intel-targets +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-start@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-stop@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-reboot@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-startmin@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-poweron@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-poweroff@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-hard-poweroff@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-powerreset@.target" diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf.bbappend b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf.bbappend new file mode 100644 index 000000000..8f26d784b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf.bbappend @@ -0,0 +1,12 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://journald.conf \ + file://systemd-timesyncd-save-time.conf \ + " + +FILES_${PN} += " ${systemd_system_unitdir}/systemd-timesyncd.service.d/systemd-timesyncd-save-time.conf" + +do_install_append() { + install -m 644 -D ${WORKDIR}/journald.conf ${D}${sysconfdir}/systemd/journald.conf + install -m 644 -D ${WORKDIR}/systemd-timesyncd-save-time.conf ${D}${systemd_system_unitdir}/systemd-timesyncd.service.d/systemd-timesyncd-save-time.conf +} diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf/journald.conf b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf/journald.conf new file mode 100644 index 000000000..5d4134cbd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf/journald.conf @@ -0,0 +1,42 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See journald.conf(5) for details. + +[Journal] +Storage=persistent +#Compress=yes +#Seal=yes +#SplitMode=uid +#SyncIntervalSec=5m +#RateLimitIntervalSec=30s +#RateLimitBurst=10000 +SystemMaxUse=6M +#SystemKeepFree= +SystemMaxFileSize=512K +# SystemMaxFiles=32 +#RuntimeMaxUse=32M +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#RuntimeMaxFiles=4 +#MaxRetentionSec= +#MaxFileSec=1month +#ForwardToSyslog=no +#ForwardToKMsg=no +#ForwardToConsole=no +#ForwardToWall=yes +#TTYPath=/dev/console +MaxLevelStore=notice +#MaxLevelSyslog=debug +#MaxLevelKMsg=notice +#MaxLevelConsole=info +#MaxLevelWall=emerg +#LineMax=48K diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf/systemd-timesyncd-save-time.conf b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf/systemd-timesyncd-save-time.conf new file mode 100644 index 000000000..aa455cbcb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd-conf/systemd-timesyncd-save-time.conf @@ -0,0 +1,2 @@ +[Service] +ExecStop=touch /var/lib/systemd/timesync/clock
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/000-ro-rootfs-tmpfile-defaults.patch b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/000-ro-rootfs-tmpfile-defaults.patch new file mode 100644 index 000000000..d16f3a2dc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/000-ro-rootfs-tmpfile-defaults.patch @@ -0,0 +1,51 @@ +From 05e1b853abfd54d117dad25185c602d1791d83f6 Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Tue, 26 Jun 2018 16:10:14 -0700 +Subject: [PATCH] ro-rootfs-tmpfile-defaults + +--- + tmpfiles.d/home.conf | 1 - + tmpfiles.d/tmp.conf | 1 - + tmpfiles.d/var.conf.m4 | 5 ++++- + 3 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/tmpfiles.d/home.conf b/tmpfiles.d/home.conf +index 9f25b83..5c7513a 100644 +--- a/tmpfiles.d/home.conf ++++ b/tmpfiles.d/home.conf +@@ -8,4 +8,3 @@ + # See tmpfiles.d(5) for details + + Q /home 0755 - - - +-q /srv 0755 - - - +diff --git a/tmpfiles.d/tmp.conf b/tmpfiles.d/tmp.conf +index 22555a0..aad1b98 100644 +--- a/tmpfiles.d/tmp.conf ++++ b/tmpfiles.d/tmp.conf +@@ -9,7 +9,6 @@ + + # Clear tmp directories separately, to make them easier to override + q /tmp 1777 root root 10d +-q /var/tmp 1777 root root 30d + + # Exclude namespace mountpoints created with PrivateTmp=yes + x /tmp/systemd-private-%b-* +diff --git a/tmpfiles.d/var.conf.m4 b/tmpfiles.d/var.conf.m4 +index 0e2c509..fa288b8 100644 +--- a/tmpfiles.d/var.conf.m4 ++++ b/tmpfiles.d/var.conf.m4 +@@ -11,7 +11,10 @@ q /var 0755 - - - + + L /var/run - - - - ../run + +-d /var/log 0755 - - - ++# now /var/log and /var/tmp really live in volatile ++L /var/log - - - - volatile/log ++L /var/tmp - - - - volatile/tmp ++ + m4_ifdef(`ENABLE_UTMP', + f /var/log/wtmp 0664 root utmp - + f /var/log/btmp 0660 root utmp - +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch new file mode 100644 index 000000000..f72052e0c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch @@ -0,0 +1,28 @@ +From 3016898f4300fdd8db74f821cd6ea54dbf39fdc8 Mon Sep 17 00:00:00 2001 +From: James Feist <james.feist@linux.intel.com> +Date: Tue, 6 Mar 2018 16:06:33 -0800 +Subject: [PATCH 1/1] Modfiy system.conf DefaultTimeoutStopSec + +Current time is 5 minutes, change it to 10 seconds. + +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + src/core/system.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/system.conf.in b/src/core/system.conf.in +index 746572b..ba2a265 100644 +--- a/src/core/system.conf.in ++++ b/src/core/system.conf.in +@@ -33,7 +33,7 @@ + #DefaultStandardOutput=journal + #DefaultStandardError=inherit + #DefaultTimeoutStartSec=90s +-#DefaultTimeoutStopSec=90s ++DefaultTimeoutStopSec=10s + #DefaultRestartSec=100ms + #DefaultStartLimitIntervalSec=10s + #DefaultStartLimitBurst=5 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd_%.bbappend new file mode 100644 index 000000000..ef7ac20e2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd_%.bbappend @@ -0,0 +1,11 @@ +# add some configuration overrides for systemd default /usr/lib/tmpfiles.d/ + +LICENSE = "GPL-2.0" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://000-ro-rootfs-tmpfile-defaults.patch \ + file://0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch \ + " + +USERADD_PACKAGES_remove = "${PN}-journal-gateway ${PN}-journal-upload ${PN}-journal-remote" diff --git a/meta-openbmc-mods/meta-common/recipes-devtools/dtoverlay/dtoverlay.bb b/meta-openbmc-mods/meta-common/recipes-devtools/dtoverlay/dtoverlay.bb new file mode 100644 index 000000000..66107d81b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-devtools/dtoverlay/dtoverlay.bb @@ -0,0 +1,31 @@ +SUMMARY = "dtoverlay" +DESCRIPTION = "device tree overlay application" + +SRC_URI = "git://github.com/raspberrypi/userland.git" +LICENSE = "BSD" +LIC_FILES_CHKSUM = "file://LICENCE;md5=0448d6488ef8cc380632b1569ee6d196" + +SRCREV = "11389772c79685442e0ab8aa9be8ad0e32703f68" +requires = "chrpath-native" + +S = "${WORKDIR}/git" + +PV = "1" + +inherit cmake + +FILES_${PN} += "${libdir} ${libdir}libdtovl.so.${PV}" + +do_compile_append(){ + chrpath -d ${S}/build/bin/dtoverlay +} + +do_install() { + install -d ${D}${libdir} + install -m 0644 ${S}/build/lib/libdtovl.so ${D}${libdir}/libdtovl.so.${PV} + install -d ${D}${bindir} + install -m 0755 ${S}/build/bin/dtoverlay ${D}${bindir}/dtoverlay + + ln -sf libdtovl.so.${PV} ${D}{libdir}libdtovl.so +} + diff --git a/meta-openbmc-mods/meta-common/recipes-devtools/mtd-util/mtd-util.bb b/meta-openbmc-mods/meta-common/recipes-devtools/mtd-util/mtd-util.bb new file mode 100644 index 000000000..f973acac6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-devtools/mtd-util/mtd-util.bb @@ -0,0 +1,20 @@ +DESCRIPTION = "OpenBMC mtd-util" + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://COPYING;md5=b77c43ae4eaf67bd73fb6452b2f113a3" + +SRC_URI = "git://git@github.com/Intel-BMC/mtd-util;protocol=ssh" + +PV = "1.0+git${SRCPV}" +SRCREV = "22a0216b5e197bb8c7264fd0be0a4bb2d5d25b90" + + +S = "${WORKDIR}/git" + +DEPENDS += "dbus openssl zlib boost microsoft-gsl" + +inherit cmake pkgconfig + +# Specify any options you want to pass to cmake using EXTRA_OECMAKE: +EXTRA_OECMAKE = "" + diff --git a/meta-openbmc-mods/meta-common/recipes-devtools/template-recipe/template-recipe.bb b/meta-openbmc-mods/meta-common/recipes-devtools/template-recipe/template-recipe.bb new file mode 100644 index 000000000..5d13a8c44 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-devtools/template-recipe/template-recipe.bb @@ -0,0 +1,18 @@ +SUMMARY = "HelloWorld app with phosphor-logging usage example." +DESCRIPTION = "NOTE: Phosphor-logging has dependencies on systemd and sdbusplus." + +SRC_URI = "git://git-amr-2.devtools.intel.com:29418/openbmc_template-recipe;protocol=ssh" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +# Modify these as desired +PV = "1.0+git${SRCPV}" +SRCREV = "2d5d731254319de8b42d6438b0ce3908dd5b0dec" + +S = "${WORKDIR}/git" + +inherit cmake + +DEPENDS = "systemd sdbusplus phosphor-logging" +RDEPENDS_${PN} = "libsystemd sdbusplus phosphor-logging" diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/libvncserver/libvncserver_%.bbappend b/meta-openbmc-mods/meta-common/recipes-graphics/libvncserver/libvncserver_%.bbappend new file mode 100644 index 000000000..a7d8d8a95 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/libvncserver/libvncserver_%.bbappend @@ -0,0 +1,21 @@ +PACKAGECONFIG_remove = "gcrypt gnutls png sdl zlib" + +TARGET_CXXFLAGS += " -Dflto" + +do_install_append() { + rm -rf ${D}${libdir}/libvncclient* +} + +inherit cmake + +# Use the latest to support obmc-ikvm +DEPENDS += "openssl lzo" +SRC_URI = "git://github.com/LibVNC/libvncserver" +SRCREV = "3348a7e42e86dfb98dd7458ad29def476cf6096f" +S = "${WORKDIR}/git" + +# Remove x11 and gtk+ that cause big image size +# Actually, these aren't needed to support obmc-ikvm +REQUIRED_DISTRO_FEATURES_remove = "x11" +DEPENDS_remove = "gtk+" +RDEPENDS_${PN}_remove = "gtk+" diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/start-ipkvm.service b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/start-ipkvm.service new file mode 100644 index 000000000..61d6cf213 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/start-ipkvm.service @@ -0,0 +1,11 @@ +[Unit] +Description=OpenBMC ipKVM daemon +StopWhenUnneeded=false + +[Service] +Restart=always +ExecStartPre=/usr/bin/create_usbhid.sh +ExecStart=/usr/bin/env obmc-ikvm -v /dev/video0 -f 10 -k /dev/hidg0 -p /dev/hidg1 + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm_git.bb b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm_git.bb new file mode 100644 index 000000000..f69382440 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm_git.bb @@ -0,0 +1,18 @@ +SUMMARY = "OpenBMC VNC server and ipKVM daemon" +DESCRIPTION = "obmc-ikvm is a vncserver for JPEG-serving V4L2 devices to allow ipKVM" +LICENSE = "GPLv2" +LIC_FILES_CHKSUM = "file://LICENSE;md5=75859989545e37968a99b631ef42722e" + +DEPENDS = " libvncserver sdbusplus sdbusplus-native phosphor-logging phosphor-dbus-interfaces autoconf-archive-native" + +SRC_URI = "git://github.com/openbmc/obmc-ikvm" +SRCREV = "f6ed0e75b05b573345e4f3eb9d80e677f98992ac" + +PR = "r1" +PR_append = "+gitr${SRCPV}" + +SYSTEMD_SERVICE_${PN} += "start-ipkvm.service" + +S = "${WORKDIR}/git" + +inherit autotools pkgconfig obmc-phosphor-systemd diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control.bb b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control.bb new file mode 100644 index 000000000..1635b41a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control.bb @@ -0,0 +1,201 @@ +SUMMARY = "Chassis Power Control service for Intel based platform" +DESCRIPTION = "Chassis Power Control service for Intel based platfrom" + +SRC_URI = "git://git@github.com/Intel-BMC/intel-chassis-control.git;protocol=ssh" +SRCREV = "d49efd0b5e1c81fd530dbd9fa0a7545f071bc823" + +S = "${WORKDIR}/git/services/chassis/" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +inherit cmake pkgconfig pythonnative +inherit obmc-phosphor-dbus-service + +DBUS_SERVICE_${PN} += "xyz.openbmc_project.Chassis.Control.Power@.service" +DBUS_SERVICE_${PN} += "xyz.openbmc_project.Chassis.Control.Chassis@.service" +DBUS_SERVICE_${PN} += "xyz.openbmc_project.Chassis.Buttons@.service" + +# Force the standby target to run these services +SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" + +POWER_TMPL_CTRL = "xyz.openbmc_project.Chassis.Control.Power@.service" +#SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +POWER_INSTFMT_CTRL = "xyz.openbmc_project.Chassis.Control.Power@{0}.service" +POWER_FMT_CTRL = "../${POWER_TMPL_CTRL}:${SYSD_TGT}.wants/${POWER_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'POWER_FMT_CTRL', 'OBMC_HOST_INSTANCES')}" + +CHASSIS_TMPL_CTRL = "xyz.openbmc_project.Chassis.Control.Chassis@.service" +#SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +CHASSIS_INSTFMT_CTRL = "xyz.openbmc_project.Chassis.Control.Chassis@{0}.service" +CHASSIS_FMT_CTRL = "../${CHASSIS_TMPL_CTRL}:${SYSD_TGT}.wants/${CHASSIS_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'CHASSIS_FMT_CTRL', 'OBMC_HOST_INSTANCES')}" + +BUTTONS_TMPL_CTRL = "xyz.openbmc_project.Chassis.Buttons@.service" +#SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +BUTTONS_INSTFMT_CTRL = "xyz.openbmc_project.Chassis.Buttons@{0}.service" +BUTTONS_FMT_CTRL = "../${BUTTONS_TMPL_CTRL}:${SYSD_TGT}.wants/${BUTTONS_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'BUTTONS_FMT_CTRL', 'OBMC_HOST_INSTANCES')}" + +SYSTEMD_SERVICE_${PN} += " \ + obmc-host-start@.target \ + obmc-host-startmin@.target \ + obmc-host-stop@.target \ + obmc-host-reboot@.target \ + obmc-chassis-poweroff@.target \ + obmc-chassis-poweron@.target \ + obmc-chassis-hard-poweroff@.target \ + obmc-host-soft-reboot@.target \ + obmc-host-warm-reset@.target \ + obmc-chassis-powerreset@.target \ + " + +RESET_TGTFMT = "obmc-chassis-powerreset@{0}.target" + +RESET_ON_TMPL = "op-reset-chassis-running@.service" +RESET_ON_INSTFMT = "op-reset-chassis-running@{0}.service" +RESET_ON_FMT = "../${RESET_ON_TMPL}:${RESET_TGTFMT}.requires/${RESET_ON_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${RESET_ON_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'RESET_ON_FMT', 'OBMC_CHASSIS_INSTANCES')}" + +RESET_ON_CHASSIS_TMPL = "op-reset-chassis-on@.service" +RESET_ON_CHASSIS_INSTFMT = "op-reset-chassis-on@{0}.service" +RESET_ON_CHASSIS_FMT = "../${RESET_ON_CHASSIS_TMPL}:${RESET_TGTFMT}.requires/${RESET_ON_CHASSIS_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${RESET_ON_CHASSIS_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'RESET_ON_CHASSIS_FMT', 'OBMC_CHASSIS_INSTANCES')}" + +# Force the standby target to run the chassis reset check target +RESET_TMPL_CTRL = "obmc-chassis-powerreset@.target" +SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +RESET_INSTFMT_CTRL = "obmc-chassis-powerreset@{0}.target" +RESET_FMT_CTRL = "../${RESET_TMPL_CTRL}:${SYSD_TGT}.wants/${RESET_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'RESET_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +START_TMPL = "intel-power-start@.service" +START_TGTFMT = "obmc-chassis-poweron@{0}.target" +START_INSTFMT = "intel-power-start@{0}.service" +START_FMT = "../${START_TMPL}:${START_TGTFMT}.requires/${START_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${START_TMPL}" + +STOP_TMPL = "intel-power-stop@.service" +STOP_TGTFMT = "obmc-chassis-poweroff@{0}.target" +STOP_INSTFMT = "intel-power-stop@{0}.service" +STOP_FMT = "../${STOP_TMPL}:${STOP_TGTFMT}.requires/${STOP_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${STOP_TMPL}" + +WARM_RESET_TMPL = "intel-power-warm-reset@.service" +WARM_RESET_TGTFMT = "obmc-host-warm-reset@{0}.target" +WARM_RESET_INSTFMT = "intel-power-warm-reset@{0}.service" +WARM_RESET_FMT = "../${WARM_RESET_TMPL}:${WARM_RESET_TGTFMT}.requires/${WARM_RESET_INSTFMT}" +WARM_RESET_LINK_FMT = "obmc-host-warm-reset@.target:${WARM_RESET_TGTFMT}" +SYSTEMD_SERVICE_${PN} += "${WARM_RESET_TMPL}" + +# Build up requires relationship for START_TGTFMT and STOP_TGTFMT +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'START_FMT', 'OBMC_CHASSIS_INSTANCES')}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'STOP_FMT', 'OBMC_CHASSIS_INSTANCES')}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'WARM_RESET_FMT', 'OBMC_CHASSIS_INSTANCES')}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'WARM_RESET_LINK_FMT', 'OBMC_CHASSIS_INSTANCES')}" + +#The main control target requires these power targets +START_TMPL_CTRL = "obmc-chassis-poweron@.target" +START_TGTFMT_CTRL = "obmc-host-startmin@{0}.target" +START_INSTFMT_CTRL = "obmc-chassis-poweron@{0}.target" +START_FMT_CTRL = "../${START_TMPL_CTRL}:${START_TGTFMT_CTRL}.requires/${START_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'START_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +# Chassis off requires host off +STOP_TMPL_CTRL = "obmc-host-stop@.target" +STOP_TGTFMT_CTRL = "obmc-chassis-poweroff@{0}.target" +STOP_INSTFMT_CTRL = "obmc-host-stop@{0}.target" +STOP_FMT_CTRL = "../${STOP_TMPL_CTRL}:${STOP_TGTFMT_CTRL}.requires/${STOP_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'STOP_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +# Hard power off requires chassis off +HARD_OFF_TMPL_CTRL = "obmc-chassis-poweroff@.target" +HARD_OFF_TGTFMT_CTRL = "obmc-chassis-hard-poweroff@{0}.target" +HARD_OFF_INSTFMT_CTRL = "obmc-chassis-poweroff@{0}.target" +HARD_OFF_FMT_CTRL = "../${HARD_OFF_TMPL_CTRL}:${HARD_OFF_TGTFMT_CTRL}.requires/${HARD_OFF_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HARD_OFF_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +# Host soft reboot to run the shutdown target +HOST_SHUTDOWN_TMPL = "obmc-host-shutdown@.target" +HOST_SOFT_REBOOT_TMPL = "obmc-host-soft-reboot@.target" +HOST_SOFT_REBOOT_TGTFMT = "obmc-host-soft-reboot@{0}.target" +HOST_SHUTDOWN_INSTFMT = "obmc-host-shutdown@{0}.target" +HOST_SOFT_REBOOT_FMT = "../${HOST_SHUTDOWN_TMPL}:${HOST_SOFT_REBOOT_TGTFMT}.requires/${HOST_SHUTDOWN_INSTFMT}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_SOFT_REBOOT_FMT', 'OBMC_HOST_INSTANCES')}" +# And also to call the host startmin service +HOST_SOFT_REBOOT_SVC = "phosphor-reboot-host@.service" +HOST_SOFT_REBOOT_SVC_INST = "phosphor-reboot-host@{0}.service" +HOST_SOFT_REBOOT_SVC_FMT = "../${HOST_SOFT_REBOOT_SVC}:${HOST_SOFT_REBOOT_TGTFMT}.requires/${HOST_SOFT_REBOOT_SVC_INST}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_SOFT_REBOOT_SVC_FMT', 'OBMC_HOST_INSTANCES')}" + +#Broadcast Host state +PRE_HOST_START_TMPL = "obmc-send-signal-pre-host-start@.service" +PRE_HOST_START_TGTFMT = "obmc-host-start-pre@{0}.target" +PRE_HOST_START_INSTFMT = "obmc-send-signal-pre-host-start@{0}.service" +PRE_HOST_START_FMT = "../${PRE_HOST_START_TMPL}:${PRE_HOST_START_TGTFMT}.requires/${PRE_HOST_START_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${PRE_HOST_START_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'PRE_HOST_START_FMT', 'OBMC_HOST_INSTANCES')}" + +POST_HOST_START_TMPL = "obmc-send-signal-post-host-start@.service" +POST_HOST_START_TGTFMT = "obmc-host-started@{0}.target" +POST_HOST_START_INSTFMT = "obmc-send-signal-post-host-start@{0}.service" +POST_HOST_START_FMT = "../${POST_HOST_START_TMPL}:${POST_HOST_START_TGTFMT}.requires/${POST_HOST_START_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${POST_HOST_START_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'POST_HOST_START_FMT', 'OBMC_HOST_INSTANCES')}" + +HOST_STARTING_TMPL = "obmc-send-signal-host-starting@.service" +HOST_STARTING_TGTFMT = "obmc-host-starting@{0}.target" +HOST_STARTING_INSTFMT = "obmc-send-signal-host-starting@{0}.service" +HOST_STARTING_FMT = "../${HOST_STARTING_TMPL}:${HOST_STARTING_TGTFMT}.requires/${HOST_STARTING_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${HOST_STARTING_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_STARTING_FMT', 'OBMC_HOST_INSTANCES')}" + +PRE_HOST_STOP_TMPL = "obmc-send-signal-pre-host-stop@.service" +PRE_HOST_STOP_TGTFMT = "obmc-host-stop-pre@{0}.target" +PRE_HOST_STOP_INSTFMT = "obmc-send-signal-pre-host-stop@{0}.service" +PRE_HOST_STOP_FMT = "../${PRE_HOST_STOP_TMPL}:${PRE_HOST_STOP_TGTFMT}.requires/${PRE_HOST_STOP_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${PRE_HOST_STOP_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'PRE_HOST_STOP_FMT', 'OBMC_HOST_INSTANCES')}" + +POST_HOST_STOP_TMPL = "obmc-send-signal-post-host-stop@.service" +POST_HOST_STOP_TGTFMT = "obmc-host-stopped@{0}.target" +POST_HOST_STOP_INSTFMT = "obmc-send-signal-post-host-stop@{0}.service" +POST_HOST_STOP_FMT = "../${POST_HOST_STOP_TMPL}:${POST_HOST_STOP_TGTFMT}.requires/${POST_HOST_STOP_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${POST_HOST_STOP_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'POST_HOST_STOP_FMT', 'OBMC_HOST_INSTANCES')}" + +HOST_STOPPING_TMPL = "obmc-send-signal-host-stopping@.service" +HOST_STOPPING_TGTFMT = "obmc-host-stopping@{0}.target" +HOST_STOPPING_INSTFMT = "obmc-send-signal-host-stopping@{0}.service" +HOST_STOPPING_FMT = "../${HOST_STOPPING_TMPL}:${HOST_STOPPING_TGTFMT}.requires/${HOST_STOPPING_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${HOST_STOPPING_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_STOPPING_FMT', 'OBMC_HOST_INSTANCES')}" + +DEPENDS += " \ + autoconf-archive-native \ + boost \ + i2c-tools \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " + +EXTRA_OECMAKE = " -DENABLE_GTEST=OFF -DCMAKE_SKIP_RPATH=ON" + +# linux-libc-headers guides this way to include custom uapi headers +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-start@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-start@.service new file mode 100644 index 000000000..763c11546 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-start@.service @@ -0,0 +1,16 @@ +[Unit] +Description=Start Power%i on +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Conflicts=obmc-chassis-poweroff@%i.target +ConditionPathExists=!/run/openbmc/chassis@%i-on + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` \ + /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power setPowerState i 1" +SyslogIdentifier=intel-power-start +StartLimitInterval=0 + +[Install] +WantedBy=obmc-host-start@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-stop@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-stop@.service new file mode 100644 index 000000000..5d0e46f82 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-stop@.service @@ -0,0 +1,20 @@ +[Unit] +Description=Stop Power%i +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Conflicts=obmc-chassis-poweron@%i.target +Conflicts=obmc-host-start@%i.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` \ + /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power setPowerState i 0" +SyslogIdentifier=intel-power-stop +StartLimitInterval=0 + +ExecStart=/bin/rm -f /run/openbmc/chassis@%i-on +ExecStart=/bin/rm -f /run/openbmc/host@%i-on +ExecStart=/bin/rm -f /run/openbmc/host@%i-request + +[Install] +WantedBy=obmc-chassis-poweroff@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-warm-reset@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-warm-reset@.service new file mode 100644 index 000000000..8d4897a25 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-warm-reset@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Power%i warm reset +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Conflicts=obmc-chassis-poweroff@%i.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` \ + /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power setPowerState i 2" +SyslogIdentifier=intel-power-warm-reset + +[Install] +WantedBy=obmc-host-warm-reset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-hard-poweroff@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-hard-poweroff@.target new file mode 100644 index 000000000..9a9902f7c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-hard-poweroff@.target @@ -0,0 +1,12 @@ +[Unit] +Description=Chassis%i (Hard Power Off) +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-chassis-poweron@%i.target +Conflicts=obmc-chassis-reset@%i.target +Conflicts=obmc-host-shutdown@%i.target +Conflicts=xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service +RefuseManualStop=yes + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweroff@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweroff@.target new file mode 100644 index 000000000..34580a21f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweroff@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Chassis%i (Power Off) +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-chassis-poweron@%i.target +Conflicts=obmc-chassis-reset@%i.target +RefuseManualStop=yes + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweron@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweron@.target new file mode 100644 index 000000000..f8fecf2a6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweron@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Chassis%i (Power On) +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-chassis-poweroff@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-powerreset@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-powerreset@.target new file mode 100644 index 000000000..8d7c47e6b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-powerreset@.target @@ -0,0 +1,7 @@ +[Unit] +Description=Chassis%i (Reset Check) +Conflicts=obmc-chassis-poweroff@%i.target +RefuseManualStop=yes + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-reboot@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-reboot@.target new file mode 100644 index 000000000..c860889e3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-reboot@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Reboot Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-startmin@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-soft-reboot@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-soft-reboot@.target new file mode 100644 index 000000000..c35c3e1ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-soft-reboot@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Soft Reboot Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-startmin@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-start@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-start@.target new file mode 100644 index 000000000..425953d4d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-start@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Start Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-stop@%i.target +RefuseManualStop=yes +OnFailure=obmc-host-quiesce@%i.target +OnFailureJobMode=flush
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-startmin@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-startmin@.target new file mode 100644 index 000000000..69056254a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-startmin@.target @@ -0,0 +1,6 @@ +[Unit] +Description=Start Host%i Minimum +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-stop@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-stop@.target new file mode 100644 index 000000000..0693db6e5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-stop@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Stop Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-startmin@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-warm-reset@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-warm-reset@.target new file mode 100644 index 000000000..8aed937e7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-warm-reset@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Warm reset Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-stop@%i.target +RefuseManualStop=yes +OnFailure=obmc-host-quiesce@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-starting@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-starting@.service new file mode 100644 index 000000000..4e84c8783 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-starting@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast host starting signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.HostStarting" +SyslogIdentifier=hoststartingsignal + +[Install] +WantedBy=obmc-host-starting@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-stopping@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-stopping@.service new file mode 100644 index 000000000..0f89f94a8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-stopping@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast host stopping signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.HostStoping" +SyslogIdentifier=hoststoppingsignal + +[Install] +WantedBy=obmc-host-stopping@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-start@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-start@.service new file mode 100644 index 000000000..f7e0a3bde --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-start@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast post host start signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PostHostStart" +SyslogIdentifier=posthoststartsignal + +[Install] +WantedBy=obmc-host-started@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-stop@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-stop@.service new file mode 100644 index 000000000..90007dbf2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-stop@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast post host stop signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PostHostStop" +SyslogIdentifier=posthoststopsignal + +[Install] +WantedBy=obmc-host-stopped@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-start@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-start@.service new file mode 100644 index 000000000..a57423e2c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-start@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast pre host start signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PreHostStart" +SyslogIdentifier=prehoststartsignal + +[Install] +WantedBy=obmc-host-start-pre@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-stop@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-stop@.service new file mode 100644 index 000000000..ec6f453cd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-stop@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Broadcast pre host stop signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PreHostStop" +SyslogIdentifier=prehoststopsignal + +[Install] +WantedBy=obmc-host-stop-pre@%i.target + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-on@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-on@.service new file mode 100644 index 000000000..d3ea71639 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-on@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Start chassis%i on after BMC reset +Requires=op-reset-chassis-running@%i.service +After=op-reset-chassis-running@%i.service +After=obmc-power-reset-on@%i.target +Requires=obmc-power-reset-on@%i.target +ConditionPathExists=/run/openbmc/chassis@%i-on + +[Service] +RemainAfterExit=no +ExecStart=/bin/systemctl start obmc-host-start@%i.target + + +[Install] +WantedBy=obmc-chassis-powerreset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-running@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-running@.service new file mode 100644 index 000000000..3280d0a40 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-running@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Check Chassis%i pgood and create a file to indicate it +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Wants=obmc-power-reset-on@%i.target +Before=obmc-power-reset-on@%i.target +Conflicts=obmc-chassis-poweroff@%i.target + +[Service] +RemainAfterExit=no +Type=oneshot +ExecStart=/bin/sh -c "if [ $(busctl get-property `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power pgood | sed 's/i\s*[1]/on/' | grep on | wc -l) != 0 ]; then mkdir -p /run/openbmc/ && touch /run/openbmc/chassis@%i-on; fi" + +[Install] +WantedBy=obmc-chassis-powerreset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Buttons@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Buttons@.service new file mode 100644 index 000000000..e1e3baedf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Buttons@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Intel Buttons%i + +[Service] +Restart=always +RestartSec=3 +ExecStart=/usr/bin/env buttons +SyslogIdentifier=buttons +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Chassis@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Chassis@.service new file mode 100644 index 000000000..521cb17b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Chassis@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Intel Chassis%i Control + +[Service] +Restart=always +ExecStart=/usr/bin/env chassis-control +SyslogIdentifier=chassis-control +Type=dbus +BusName={BUSNAME} +Nice=19 +TimeoutStartSec=180s + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Power@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Power@.service new file mode 100644 index 000000000..985479401 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Power@.service @@ -0,0 +1,15 @@ + +[Unit] +Description=Intel Power Control%i + +[Service] +Restart=always +RestartSec=3 +ExecStart=/usr/bin/env power-control +SyslogIdentifier=power-control +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/images/intel-platforms.bb b/meta-openbmc-mods/meta-common/recipes-intel/images/intel-platforms.bb new file mode 100644 index 000000000..276e549b9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/images/intel-platforms.bb @@ -0,0 +1,13 @@ +DESCRIPTION = "Image with Intel content based upon Phosphor, an OpenBMC framework." + +inherit obmc-phosphor-full-fitimage +inherit obmc-phosphor-image-common +inherit obmc-phosphor-image-dev + +FEATURE_PACKAGES_obmc-sensors = "" + +fix_shadow_perms() { + chgrp shadow ${IMAGE_ROOTFS}${sysconfdir}/shadow + chmod u=rw,g+r ${IMAGE_ROOTFS}${sysconfdir}/shadow +} +ROOTFS_POSTPROCESS_COMMAND += "fix_shadow_perms ; " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-intel-apps.bb b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-intel-apps.bb new file mode 100644 index 000000000..fdaf60b41 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-intel-apps.bb @@ -0,0 +1,44 @@ +SUMMARY = "OpenBMC for Intel - Applications" +PR = "r1" + +inherit packagegroup + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +PROVIDES = "${PACKAGES}" +PACKAGES = " \ + ${PN}-chassis \ + ${PN}-fans \ + ${PN}-flash \ + ${PN}-system \ + " + +PROVIDES += "virtual/obmc-chassis-mgmt" +PROVIDES += "virtual/obmc-fan-mgmt" +PROVIDES += "virtual/obmc-flash-mgmt" +PROVIDES += "virtual/obmc-system-mgmt" + +RPROVIDES_${PN}-chassis += "virtual-obmc-chassis-mgmt" +RPROVIDES_${PN}-fans += "virtual-obmc-fan-mgmt" +RPROVIDES_${PN}-flash += "virtual-obmc-flash-mgmt" +RPROVIDES_${PN}-system += "virtual-obmc-system-mgmt" + +SUMMARY_${PN}-chassis = "Intel Chassis" +RDEPENDS_${PN}-chassis = " \ + intel-chassis-control \ + libgpiod \ + obmc-host-failure-reboots \ + " + +SUMMARY_${PN}-fans = "Intel Fans" +RDEPENDS_${PN}-fans = " \ + phosphor-pid-control \ + " + +SUMMARY_${PN}-flash = "Intel Flash" +RDEPENDS_${PN}-flash = " \ + obmc-flash-bmc \ + obmc-mgr-download \ + obmc-control-bmc \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-obmc-apps.bbappend b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-obmc-apps.bbappend new file mode 100644 index 000000000..db0ec4688 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-obmc-apps.bbappend @@ -0,0 +1,6 @@ +# this is here to keep obmc-mgr-system happy, +# power control should stop relying on the deprcated +# package then we can remove it obmc-mgr-inventory +RDEPENDS_${PN}-inventory += "obmc-mgr-inventory" +# this is for image signing and signature verification +RDEPENDS_${PN}-extras += "${@bb.utils.contains('IMAGE_TYPE', 'pfr', ' phosphor-image-signing', '', d)}"
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1.bb b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1.bb new file mode 100644 index 000000000..323b2f18c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1.bb @@ -0,0 +1,33 @@ +SUMMARY = "SMBIOS MDR version 1 service for Intel based platform" +DESCRIPTION = "SMBIOS MDR version 1 service for Intel based platfrom" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "f4d4bfc3296cb27feb17aa5d1d93b3061b56ce10" + +S = "${WORKDIR}/git/services/smbios/" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +inherit cmake pkgconfig pythonnative +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE_${PN} += "smbios-mdrv1.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1/smbios-mdrv1.service b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1/smbios-mdrv1.service new file mode 100644 index 000000000..edfd3bf70 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1/smbios-mdrv1.service @@ -0,0 +1,13 @@ +[Unit] +Description=Intel BMC SMBIOS MDR V1 + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStartPre=/bin/mkdir -p /etc/smbios +ExecStart=/usr/bin/env smbiosapp +SyslogIdentifier=smbiosapp + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2.bb b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2.bb new file mode 100644 index 000000000..0cf0a7c32 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2.bb @@ -0,0 +1,33 @@ +SUMMARY = "SMBIOS MDR version 2 service for Intel based platform" +DESCRIPTION = "SMBIOS MDR version 2 service for Intel based platfrom" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "f4d4bfc3296cb27feb17aa5d1d93b3061b56ce10" + +S = "${WORKDIR}/git/services/smbios-mdrv2/" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +inherit cmake pkgconfig pythonnative +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE_${PN} += "smbios-mdrv2.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2/smbios-mdrv2.service b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2/smbios-mdrv2.service new file mode 100644 index 000000000..b72873406 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2/smbios-mdrv2.service @@ -0,0 +1,12 @@ +[Unit] +Description=Intel BMC SMBIOS MDR V2 + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStart=/usr/bin/env smbiosmdrv2app +SyslogIdentifier=smbiosmdrv2app + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch new file mode 100644 index 000000000..2a94453b3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch @@ -0,0 +1,56 @@ +From 2affc8ab570c9d1e6d6e5ecbdbeddbc5e3b15cc5 Mon Sep 17 00:00:00 2001 +From: Juston Li <juston.li@intel.com> +Date: Mon, 27 Mar 2017 11:16:00 -0700 +Subject: [PATCH] arm: dts: aspeed-g5: add espi + +Change-Id: I0b607657883619a3acefdbf344d39bf01790c4b1 +Signed-off-by: Juston Li <juston.li@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index e4c5de3208e0..a3c456ba3f34 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -260,13 +260,22 @@ + gpio-controller; + compatible = "aspeed,ast2500-gpio"; +- reg = <0x1e780000 0x1000>; ++ reg = <0x1e780000 0x0200>; + interrupts = <20>; + gpio-ranges = <&pinctrl 0 0 220>; + clocks = <&syscon ASPEED_CLK_APB>; + interrupt-controller; + #interrupt-cells = <2>; + }; + ++ sgpio: sgpio@1e780200 { ++ #gpio-cells = <2>; ++ gpio-controller; ++ compatible = "aspeed,ast2500-sgpio"; ++ reg = <0x1e780200 0x0100>; ++ interrupts = <40>; ++ interrupt-controller; ++ }; ++ + rtc: rtc@1e781000 { + compatible = "aspeed,ast2500-rtc"; + reg = <0x1e781000 0x18>; +@@ -342,6 +351,13 @@ + status = "disabled"; + }; + ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2500-espi-slave"; ++ reg = <0x1e6ee000 0x100>; ++ interrupts = <23>; ++ status = "disabled"; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2500-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch new file mode 100644 index 000000000..2ac429a22 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch @@ -0,0 +1,125 @@ +From 074f1c74fde88aac3a10059e4928919782cd40d6 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Mon, 4 Jun 2018 13:45:42 -0700 +Subject: [PATCH] New flash map for Intel + =================================================================== + +--- + .../boot/dts/openbmc-flash-layout-intel-128MB.dtsi | 58 ++++++++++++++++++++++ + .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 39 +++++++++++++++ + 2 files changed, 97 insertions(+) + create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi + create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi + +diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi +new file mode 100644 +index 0000000..23426ac +--- /dev/null ++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// 128MB flash layout: PFR (active + tmp1/tmp2 + extra) ++// image with common RW partition ++ ++partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u-boot@0 { ++ reg = <0x0 0x80000>; ++ label = "u-boot"; ++ }; ++ ++ fit-image-a@80000 { ++ reg = <0x80000 0x1b80000>; ++ label = "image-a"; ++ }; ++ ++ sofs@1c00000 { ++ reg = <0x1c00000 0x200000>; ++ label = "sofs"; ++ }; ++ ++ rwfs@1e00000 { ++ reg = <0x1e00000 0x600000>; ++ label = "rwfs"; ++ }; ++ ++ u-boot-env@2400000 { ++ reg = <0x2400000 0x20000>; ++ label = "u-boot-env"; ++ }; ++ ++ /* ++ pfr-resvd@1260000 { ++ reg = <0x2460000 0x20000>; ++ label = "pfr-resvd"; ++ }; ++ */ ++ ++ rc1@2480000 { ++ reg = <0x2480000 0x1b80000>; ++ label = "rc1"; ++ }; ++ ++ rc2@4000000 { ++ reg = <0x4000000 0x1b80000>; ++ label = "rc2"; ++ }; ++ ++ bios-staging@6000000 { ++ reg = <0x6000000 0x2000000>; ++ label = "bios-staging"; ++ }; ++}; ++ ++ +diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi +new file mode 100644 +index 0000000..6ae8e57 +--- /dev/null ++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// 64MB flash layout: redundant image with common RW partition ++ ++partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u-boot@0 { ++ reg = <0x0 0x80000>; ++ label = "u-boot"; ++ }; ++ ++ fit-image-a@80000 { ++ reg = <0x80000 0x1b80000>; ++ label = "image-a"; ++ }; ++ ++ sofs@1c00000 { ++ reg = <0x1c00000 0x200000>; ++ label = "sofs"; ++ }; ++ ++ rwfs@1e00000 { ++ reg = <0x1e00000 0x600000>; ++ label = "rwfs"; ++ }; ++ ++ u-boot-env@2400000 { ++ reg = <0x2400000 0x20000>; ++ label = "u-boot-env"; ++ }; ++ ++ fit-image-b@2480000 { ++ reg = <0x2480000 0x1b80000>; ++ label = "image-b"; ++ }; ++}; ++ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch new file mode 100644 index 000000000..78824dde7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch @@ -0,0 +1,474 @@ +From 42505ffb3c24b3e7f8182af520ab1c10a3b3f3c4 Mon Sep 17 00:00:00 2001 +From: "Feist, James" <james.feist@intel.com> +Date: Mon, 5 Jun 2017 11:13:52 -0700 +Subject: [PATCH] Add ASPEED SGPIO driver. + +Port aspeed sgpio driver to OBMC Kernel and +enable it on Purley config. Based off AST sdk 4.0. + +Change-Id: I8529c3fb001ea6f93e63b269cdcdde3887a84e40 +Signed-off-by: James Feist <james.feist@linux.intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/gpio/Kconfig | 8 + + drivers/gpio/Makefile | 1 + + drivers/gpio/sgpio-aspeed.c | 416 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 425 insertions(+) + create mode 100644 drivers/gpio/sgpio-aspeed.c + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 71c0ab46f216..a0485be99db7 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -124,6 +124,14 @@ config GPIO_ASPEED + help + Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. + ++config SGPIO_ASPEED ++ bool "ASPEED SGPIO support" ++ depends on ARCH_ASPEED ++ select GPIO_GENERIC ++ select GPIOLIB_IRQCHIP ++ help ++ Say Y here to support ASPEED SGPIO functionality. ++ + config GPIO_ATH79 + tristate "Atheros AR71XX/AR724X/AR913X GPIO support" + default y if ATH79 +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 1324c8f966a7..23b8d29bef70 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o + obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o + obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o + obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o ++obj-$(CONFIG_SGPIO_ASPEED) += sgpio-aspeed.o + obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o + obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o + obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o +diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c +new file mode 100644 +index 000000000000..9c4add74602a +--- /dev/null ++++ b/drivers/gpio/sgpio-aspeed.c +@@ -0,0 +1,416 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2018 Intel Corporation ++ ++#include <asm/mach/irq.h> ++#include <linux/bitfield.h> ++#include <linux/gpio/driver.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of_gpio.h> ++#include <linux/platform_device.h> ++ ++#ifdef ARCH_NR_GPIOS ++#undef ARCH_NR_GPIOS ++#endif ++ ++// TODO: move this to aspeed_sgpio_of_table ++#if defined(CONFIG_MACH_ASPEED_G5) ++#define GPIO_PORT_NUM 29 ++#elif defined(CONFIG_MACH_ASPEED_G4) ++#define GPIO_PORT_NUM 28 ++#endif ++ ++// TODO: fix defines ++#define GPIOS_PER_PORT 8 ++#define ARCH_NR_GPIOS (GPIOS_PER_PORT * GPIO_PORT_NUM) ++#define ASPEED_SGPIO_CTRL 0x54 ++#define SGPIO_CHAIN_CHIP_BASE ARCH_NR_GPIOS ++#define SGPIO_GROUP_NUMS 10 ++ ++#define ASPEED_VIC_NUMS 64 ++#define ASPEED_FIQ_NUMS 64 ++#define ARCH_NR_I2C 14 ++ ++#define IRQ_I2C_CHAIN_START (ASPEED_VIC_NUMS + ASPEED_FIQ_NUMS) ++#define IRQ_GPIO_CHAIN_START (IRQ_I2C_CHAIN_START + ARCH_NR_I2C) ++#define IRQ_SGPIO_CHAIN_START (IRQ_GPIO_CHAIN_START + ARCH_NR_GPIOS) ++ ++#define ASPEED_SGPIO_PINS_MASK GENMASK(9, 6) ++#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) ++#define ASPEED_SGPIO_ENABLE BIT(0) ++ ++struct aspeed_sgpio { ++ struct device *dev; ++ int mirq; /* master irq */ ++ void __iomem *base; ++// TODO: make below members as a struct member ++ uint index; ++ uint data_offset; ++ uint data_read_offset; ++ uint int_en_offset; ++ uint int_type_offset; ++ uint int_sts_offset; ++ uint rst_tol_offset; ++ struct gpio_chip chip; ++}; ++ ++uint aspeed_sgpio_to_irq(uint gpio) ++{ ++ return (gpio + IRQ_SGPIO_CHAIN_START); ++} ++EXPORT_SYMBOL(aspeed_sgpio_to_irq); ++ ++uint aspeed_irq_to_sgpio(uint irq) ++{ ++ return (irq - IRQ_SGPIO_CHAIN_START); ++} ++EXPORT_SYMBOL(aspeed_irq_to_sgpio); ++ ++static int aspeed_sgpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct aspeed_sgpio *priv = gpiochip_get_data(chip); ++ uint bit_nr = priv->index * GPIOS_PER_PORT + offset; ++ unsigned long flags; ++ u32 v; ++ ++ local_irq_save(flags); ++ ++ v = readl(priv->base + priv->data_offset); ++ v &= BIT(bit_nr); ++ ++ if (v) ++ v = 1; ++ else ++ v = 0; ++ ++ local_irq_restore(flags); ++ ++ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label, ++ offset, v); ++ ++ return v; ++} ++ ++static void aspeed_sgpio_set(struct gpio_chip *chip, unsigned offset, int val) ++{ ++ struct aspeed_sgpio *priv = gpiochip_get_data(chip); ++ uint bit_nr = priv->index * GPIOS_PER_PORT + offset; ++ unsigned long flags; ++ u32 v; ++ ++ local_irq_save(flags); ++ ++ v = readl(priv->base + priv->data_read_offset); ++ ++ if (val) ++ v |= BIT(bit_nr); ++ else ++ v &= ~BIT(bit_nr); ++ ++ writel(v, priv->base + priv->data_offset); ++ ++ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label, ++ offset, val); ++ ++ local_irq_restore(flags); ++} ++ ++#define SGPIO_BANK(name, index_no, data, read_data, int_en, int_type, \ ++ int_sts, rst_tol, chip_base_idx) { \ ++ .index = index_no, \ ++ .data_offset = data, \ ++ .data_read_offset = read_data, \ ++ .int_en_offset = int_en, \ ++ .int_type_offset = int_type, \ ++ .int_sts_offset = int_sts, \ ++ .rst_tol_offset = rst_tol, \ ++ .chip = { \ ++ .label = name, \ ++ .get = aspeed_sgpio_get, \ ++ .set = aspeed_sgpio_set, \ ++ .base = SGPIO_CHAIN_CHIP_BASE + chip_base_idx * 8, \ ++ .ngpio = GPIOS_PER_PORT, \ ++ }, \ ++} ++ ++// TODO: use a single priv after changing it as an array member of the priv ++static struct aspeed_sgpio aspeed_sgpio_gp[] = { ++ SGPIO_BANK("SGPIOA", 0, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 0), ++ SGPIO_BANK("SGPIOB", 1, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 1), ++ SGPIO_BANK("SGPIOC", 2, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 2), ++ SGPIO_BANK("SGPIOD", 3, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 3), ++ SGPIO_BANK("SGPIOE", 0, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 4), ++ SGPIO_BANK("SGPIOF", 1, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 5), ++ SGPIO_BANK("SGPIOG", 2, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 6), ++ SGPIO_BANK("SGPIOH", 3, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 7), ++ SGPIO_BANK("SGPIOI", 0, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 8), ++ SGPIO_BANK("SGPIOJ", 1, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 9), ++}; ++ ++/** ++ * We need to unmask the GPIO bank interrupt as soon as possible to avoid ++ * missing GPIO interrupts for other lines in the bank. Then we need to ++ * mask-read-clear-unmask the triggered GPIO lines in the bank to avoid missing ++ * nested interrupts for a GPIO line. If we wait to unmask individual GPIO lines ++ * in the bank after the line's interrupt handler has been run, we may miss some ++ * nested interrupts. ++ */ ++static void aspeed_sgpio_irq_handler(struct irq_desc *desc) ++{ ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct aspeed_sgpio *priv = irq_desc_get_chip_data(desc); ++ u32 isr; ++ int i; ++ ++ chained_irq_enter(chip, desc); ++ ++ isr = readl(priv->base + priv->int_sts_offset); ++ isr = (isr >> (GPIOS_PER_PORT * priv->index)) & 0xff; ++ ++ dev_dbg(priv->dev, "[%s] isr %x \n", priv->chip.label, isr); ++ ++ if (isr != 0) { ++ for (i = 0; i < GPIOS_PER_PORT; i++) { ++ if (BIT(i) & isr) ++ generic_handle_irq(i * GPIOS_PER_PORT + ++ i + IRQ_SGPIO_CHAIN_START); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void aspeed_sgpio_ack_irq(struct irq_data *d) ++{ ++ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq); ++ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq; ++ ++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__, ++ priv->chip.label, sgpio_irq); ++ ++ writel(BIT(bit_nr), priv->base + priv->int_sts_offset); ++} ++ ++static void aspeed_sgpio_mask_irq(struct irq_data *d) ++{ ++ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq); ++ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq; ++ ++ /* Disable IRQ */ ++ writel(readl(priv->base + priv->int_en_offset) & ~BIT(bit_nr), ++ priv->base + priv->int_en_offset); ++ ++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n ", d->irq, __func__, ++ priv->chip.label, sgpio_irq); ++} ++ ++static void aspeed_sgpio_unmask_irq(struct irq_data *d) ++{ ++ struct aspeed_sgpio *priv = irq_data_get_irq_chip_data(d); ++ u32 sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq; ++ ++ /* Enable IRQ */ ++ writel(BIT(bit_nr), priv->base + priv->int_sts_offset); ++ writel(readl(priv->base + priv->int_en_offset) | BIT(bit_nr), ++ priv->base + priv->int_en_offset); ++ ++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__, ++ priv->chip.label, sgpio_irq); ++} ++ ++static int aspeed_sgpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ unsigned int irq = d->irq; ++ struct aspeed_sgpio *priv = irq_get_chip_data(irq); ++ u32 sgpio_irq = (irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ u32 type0, type1, type2; ++ ++ dev_dbg(priv->dev, "irq: %d, sgpio_irq: %d , irq_type: 0x%x\n", ++ irq, sgpio_irq, type); ++ ++ if (type & ~IRQ_TYPE_SENSE_MASK) ++ return -EINVAL; ++ ++ type0 = readl(priv->base + priv->int_type_offset); ++ type1 = readl(priv->base + priv->int_type_offset + 0x04); ++ type2 = readl(priv->base + priv->int_type_offset + 0x08); ++ ++ switch (type) { ++ /* Edge rising type */ ++ case IRQ_TYPE_EDGE_RISING: ++ type0 |= BIT(sgpio_irq); ++ type1 &= ~BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ /* Edge falling type */ ++ case IRQ_TYPE_EDGE_FALLING: ++ type2 |= BIT(sgpio_irq); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ type0 &= ~BIT(sgpio_irq); ++ type1 |= BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ type0 |= BIT(sgpio_irq); ++ type1 |= BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ type0 &= ~BIT(sgpio_irq); ++ type1 |= BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ default: ++ dev_dbg(priv->dev, "not supported trigger type: %d", type); ++ return -EINVAL; ++ break; ++ } ++ ++ writel(type0, priv->base + priv->int_type_offset); ++ writel(type1, priv->base + priv->int_type_offset + 0x04); ++ writel(type2, priv->base + priv->int_type_offset + 0x08); ++ ++ return 0; ++} ++ ++static struct irq_chip aspeed_sgpio_irq_chip = { ++ .name = "aspeed-sgpio", ++ .irq_ack = aspeed_sgpio_ack_irq, ++ .irq_mask = aspeed_sgpio_mask_irq, ++ .irq_unmask = aspeed_sgpio_unmask_irq, ++ .irq_set_type = aspeed_sgpio_irq_type, ++}; ++ ++static int aspeed_sgpio_config(struct aspeed_sgpio *priv) ++{ ++ /** ++ * There is a limitation that SGPIO clock division has to be larger or ++ * equal to 1. And the value of clock division read back is left shift ++ * 1 bit from actual value. ++ * ++ * GPIO254[31:16] - Serial GPIO clock division ++ * Serial GPIO clock period = period of PCLK * 2 * (GPIO254[31:16] + 1) ++ * ++ * SGPIO master controller updates every data input when SGPMLD is low. ++ * For example, SGPIO clock is 1MHz and number of SGPIO is 80 then each ++ * SGPIO will be updated in every 80us. ++ */ ++ writel(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, 10) | ++ FIELD_PREP(ASPEED_SGPIO_PINS_MASK, SGPIO_GROUP_NUMS) | ++ ASPEED_SGPIO_ENABLE, ++ priv->base + ASPEED_SGPIO_CTRL); ++ dev_dbg(priv->dev, "sgpio config reg: 0x%08X\n", ++ readl(priv->base + ASPEED_SGPIO_CTRL)); ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_probe(struct platform_device *pdev) ++{ ++ int i, j; ++ uint irq; ++ struct resource *res; ++ struct aspeed_sgpio *priv; ++ void __iomem *base; ++ int mirq; ++ ++ // aspeed_scu_multi_func_sgpio(); done via pinctl ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ mirq = platform_get_irq(pdev, 0); ++ if (!mirq) ++ return -ENODEV; ++ ++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_gp); i++) { ++ // TODO: use heap allocation and use a single priv ++ priv = &aspeed_sgpio_gp[i]; ++ priv->dev = &pdev->dev; ++ priv->base = base; ++ priv->mirq = mirq; ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ dev_dbg(priv->dev, "add gpio_chip [%s]: %d\n", priv->chip.label, ++ i); ++ ++ devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); ++ ++ /* Disable Interrupt & Clear Status & Set Level-High Trigger */ ++ writel(0x00000000, priv->base + priv->int_en_offset); ++ writel(0xffffffff, priv->base + priv->int_sts_offset); ++ writel(0xffffffff, priv->base + priv->int_type_offset); ++ writel(0xffffffff, priv->base + priv->int_type_offset + 0x04); ++ writel(0x00000000, priv->base + priv->int_type_offset + 0x08); ++ ++ // TODO: no this many chip registration is needed. fix it. ++ for (j = 0; j < GPIOS_PER_PORT; j++) { ++ irq = i * GPIOS_PER_PORT + j + IRQ_SGPIO_CHAIN_START; ++ dev_dbg(priv->dev, "inst chip data %d\n", irq); ++ irq_set_chip_data(irq, priv); ++ irq_set_chip_and_handler(irq, &aspeed_sgpio_irq_chip, ++ handle_level_irq); ++ irq_clear_status_flags(irq, IRQ_NOREQUEST); ++ } ++ } ++ ++ irq_set_chained_handler(priv->mirq, aspeed_sgpio_irq_handler); ++ ++ aspeed_sgpio_config(priv); ++ ++ dev_info(&pdev->dev, "sgpio controller registered, irq %d\n", ++ priv->mirq); ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_remove(struct platform_device *pdev) ++{ ++ struct aspeed_sgpio *priv = ++ &aspeed_sgpio_gp[ARRAY_SIZE(aspeed_sgpio_gp) - 1]; ++ ++ irq_set_chained_handler(priv->mirq, NULL); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_sgpio_of_table[] = { ++ { .compatible = "aspeed,ast2400-sgpio", }, ++ { .compatible = "aspeed,ast2500-sgpio", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table); ++ ++static struct platform_driver aspeed_sgpio_driver = { ++ .probe = aspeed_sgpio_probe, ++ .remove = aspeed_sgpio_remove, ++ .driver = { ++ .name = "sgpio-aspeed", ++ .of_match_table = of_match_ptr(aspeed_sgpio_of_table), ++ }, ++}; ++ ++static int __init aspeed_sgpio_init(void) ++{ ++ return platform_driver_register(&aspeed_sgpio_driver); ++} ++subsys_initcall(aspeed_sgpio_init); ++ ++static void __exit aspeed_sgpio_exit(void) ++{ ++ platform_driver_unregister(&aspeed_sgpio_driver); ++} ++module_exit(aspeed_sgpio_exit); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED SGPIO driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch new file mode 100644 index 000000000..346b9e3e3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch @@ -0,0 +1,240 @@ +From f4b91f5c6723e56e106a609cdbcc8da48c56499e Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Wed, 16 May 2018 10:03:14 -0700 +Subject: [PATCH] SGPIO DT and pinctrl fixup + +This commit fixes DT and pinctrl for SGPIO use. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 54 ++++++++++-------------------- + arch/arm/boot/dts/aspeed-g5.dtsi | 8 +++++ + drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 48 +++++++++++++------------- + drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 4 +++ + 4 files changed, 54 insertions(+), 60 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 6af12872ee74..9aed0f696a98 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -201,6 +201,18 @@ + interrupt-controller; + }; + ++ sgpio: sgpio@1e780200 { ++ #gpio-cells = <2>; ++ gpio-controller; ++ compatible = "aspeed,ast2400-sgpio"; ++ reg = <0x1e780200 0x0100>; ++ interrupts = <40>; ++ interrupt-controller; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgpm_default>; ++ status = "disabled"; ++ }; ++ + timer: timer@1e782000 { + /* This timer is a Faraday FTTMR010 derivative */ + compatible = "aspeed,ast2400-timer"; +@@ -1150,44 +1162,14 @@ + groups = "SD2"; + }; + +- pinctrl_sgpmck_default: sgpmck_default { +- function = "SGPMCK"; +- groups = "SGPMCK"; +- }; +- +- pinctrl_sgpmi_default: sgpmi_default { +- function = "SGPMI"; +- groups = "SGPMI"; +- }; +- +- pinctrl_sgpmld_default: sgpmld_default { +- function = "SGPMLD"; +- groups = "SGPMLD"; +- }; +- +- pinctrl_sgpmo_default: sgpmo_default { +- function = "SGPMO"; +- groups = "SGPMO"; +- }; +- +- pinctrl_sgpsck_default: sgpsck_default { +- function = "SGPSCK"; +- groups = "SGPSCK"; +- }; +- +- pinctrl_sgpsi0_default: sgpsi0_default { +- function = "SGPSI0"; +- groups = "SGPSI0"; +- }; +- +- pinctrl_sgpsi1_default: sgpsi1_default { +- function = "SGPSI1"; +- groups = "SGPSI1"; ++ pinctrl_sgpm_default: sgpm_default { ++ function = "SGPM"; ++ groups = "SGPM"; + }; + +- pinctrl_sgpsld_default: sgpsld_default { +- function = "SGPSLD"; +- groups = "SGPSLD"; ++ pinctrl_sgps_default: sgps_default { ++ function = "SGPS"; ++ groups = "SGPS"; + }; + + pinctrl_sioonctrl_default: sioonctrl_default { +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 01e901031bd4..36d72c91a2ad 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -274,6 +274,9 @@ + reg = <0x1e780200 0x0100>; + interrupts = <40>; + interrupt-controller; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgpm_default>; ++ status = "disabled"; + }; + + timer: timer@1e782000 { +@@ -1324,6 +1327,11 @@ + groups = "SDA2"; + }; + ++ pinctrl_sgpm_default: sgpm_default { ++ function = "SGPM"; ++ groups = "SGPM"; ++ }; ++ + pinctrl_sgps1_default: sgps1_default { + function = "SGPS1"; + groups = "SGPS1"; +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +index 05b153034517..353af05b8602 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +@@ -401,16 +401,22 @@ SSSF_PIN_DECL(E16, GPIOF6, TXD4, SIG_DESC_SET(SCU80, 30)); + SSSF_PIN_DECL(C17, GPIOF7, RXD4, SIG_DESC_SET(SCU80, 31)); + + #define A14 48 +-SSSF_PIN_DECL(A14, GPIOG0, SGPSCK, SIG_DESC_SET(SCU84, 0)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSCK, SGPS, SIG_DESC_SET(SCU84, 0)); ++SS_PIN_DECL(A14, GPIOG0, SGPSCK); + + #define E13 49 +-SSSF_PIN_DECL(E13, GPIOG1, SGPSLD, SIG_DESC_SET(SCU84, 1)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSLD, SGPS, SIG_DESC_SET(SCU84, 1)); ++SS_PIN_DECL(E13, GPIOG1, SGPSLD); + + #define D13 50 +-SSSF_PIN_DECL(D13, GPIOG2, SGPSI0, SIG_DESC_SET(SCU84, 2)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSIO, SGPS, SIG_DESC_SET(SCU84, 2)); ++SS_PIN_DECL(D13, GPIOG2, SGPSIO); + + #define C13 51 +-SSSF_PIN_DECL(C13, GPIOG3, SGPSI1, SIG_DESC_SET(SCU84, 3)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSI1, SGPS, SIG_DESC_SET(SCU84, 3)); ++SS_PIN_DECL(C13, GPIOG3, SGPSI1); ++ ++FUNC_GROUP_DECL(SGPS, A14, E13, D13, C13); + + #define B13 52 + SIG_EXPR_LIST_DECL_SINGLE(OSCCLK, OSCCLK, SIG_DESC_SET(SCU2C, 1)); +@@ -576,16 +582,22 @@ FUNC_GROUP_DECL(SPI1PASSTHRU, C22, G18, D19, C20, B22, G19, C18, E20); + FUNC_GROUP_DECL(VGABIOS_ROM, B22, G19, C18, E20); + + #define J5 72 +-SSSF_PIN_DECL(J5, GPIOJ0, SGPMCK, SIG_DESC_SET(SCU84, 8)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMCK, SGPM, SIG_DESC_SET(SCU84, 8)); ++SS_PIN_DECL(J5, GPIOJ0, SGPMCK); + + #define J4 73 +-SSSF_PIN_DECL(J4, GPIOJ1, SGPMLD, SIG_DESC_SET(SCU84, 9)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMLD, SGPM, SIG_DESC_SET(SCU84, 9)); ++SS_PIN_DECL(J4, GPIOJ1, SGPMLD); + + #define K5 74 +-SSSF_PIN_DECL(K5, GPIOJ2, SGPMO, SIG_DESC_SET(SCU84, 10)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMO, SGPM, SIG_DESC_SET(SCU84, 10)); ++SS_PIN_DECL(K5, GPIOJ2, SGPMO); + + #define J3 75 +-SSSF_PIN_DECL(J3, GPIOJ3, SGPMI, SIG_DESC_SET(SCU84, 11)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMI, SGPM, SIG_DESC_SET(SCU84, 11)); ++SS_PIN_DECL(J3, GPIOJ3, SGPMI); ++ ++FUNC_GROUP_DECL(SGPM, J5, J4, K5, J3); + + #define T4 76 + SSSF_PIN_DECL(T4, GPIOJ4, VGAHS, SIG_DESC_SET(SCU84, 12)); +@@ -2083,14 +2095,8 @@ static const struct aspeed_pin_group aspeed_g4_groups[] = { + ASPEED_PINCTRL_GROUP(SALT4), + ASPEED_PINCTRL_GROUP(SD1), + ASPEED_PINCTRL_GROUP(SD2), +- ASPEED_PINCTRL_GROUP(SGPMCK), +- ASPEED_PINCTRL_GROUP(SGPMI), +- ASPEED_PINCTRL_GROUP(SGPMLD), +- ASPEED_PINCTRL_GROUP(SGPMO), +- ASPEED_PINCTRL_GROUP(SGPSCK), +- ASPEED_PINCTRL_GROUP(SGPSI0), +- ASPEED_PINCTRL_GROUP(SGPSI1), +- ASPEED_PINCTRL_GROUP(SGPSLD), ++ ASPEED_PINCTRL_GROUP(SGPM), ++ ASPEED_PINCTRL_GROUP(SGPS), + ASPEED_PINCTRL_GROUP(SIOONCTRL), + ASPEED_PINCTRL_GROUP(SIOPBI), + ASPEED_PINCTRL_GROUP(SIOPBO), +@@ -2238,14 +2244,8 @@ static const struct aspeed_pin_function aspeed_g4_functions[] = { + ASPEED_PINCTRL_FUNC(SALT4), + ASPEED_PINCTRL_FUNC(SD1), + ASPEED_PINCTRL_FUNC(SD2), +- ASPEED_PINCTRL_FUNC(SGPMCK), +- ASPEED_PINCTRL_FUNC(SGPMI), +- ASPEED_PINCTRL_FUNC(SGPMLD), +- ASPEED_PINCTRL_FUNC(SGPMO), +- ASPEED_PINCTRL_FUNC(SGPSCK), +- ASPEED_PINCTRL_FUNC(SGPSI0), +- ASPEED_PINCTRL_FUNC(SGPSI1), +- ASPEED_PINCTRL_FUNC(SGPSLD), ++ ASPEED_PINCTRL_FUNC(SGPM), ++ ASPEED_PINCTRL_FUNC(SGPS), + ASPEED_PINCTRL_FUNC(SIOONCTRL), + ASPEED_PINCTRL_FUNC(SIOPBI), + ASPEED_PINCTRL_FUNC(SIOPBO), +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +index 187abd7693cf..0c89647f166f 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +@@ -577,6 +577,8 @@ SS_PIN_DECL(N3, GPIOJ2, SGPMO); + SIG_EXPR_LIST_DECL_SINGLE(SGPMI, SGPM, SIG_DESC_SET(SCU84, 11)); + SS_PIN_DECL(N4, GPIOJ3, SGPMI); + ++FUNC_GROUP_DECL(SGPM, R2, L2, N3, N4); ++ + #define N5 76 + SIG_EXPR_LIST_DECL_SINGLE(VGAHS, VGAHS, SIG_DESC_SET(SCU84, 12)); + SIG_EXPR_LIST_DECL_SINGLE(DASHN5, DASHN5, SIG_DESC_SET(SCU94, 8)); +@@ -2127,6 +2129,7 @@ static const struct aspeed_pin_group aspeed_g5_groups[] = { + ASPEED_PINCTRL_GROUP(SD2), + ASPEED_PINCTRL_GROUP(SDA1), + ASPEED_PINCTRL_GROUP(SDA2), ++ ASPEED_PINCTRL_GROUP(SGPM), + ASPEED_PINCTRL_GROUP(SGPS1), + ASPEED_PINCTRL_GROUP(SGPS2), + ASPEED_PINCTRL_GROUP(SIOONCTRL), +@@ -2296,6 +2299,7 @@ static const struct aspeed_pin_function aspeed_g5_functions[] = { + ASPEED_PINCTRL_FUNC(SD2), + ASPEED_PINCTRL_FUNC(SDA1), + ASPEED_PINCTRL_FUNC(SDA2), ++ ASPEED_PINCTRL_FUNC(SGPM), + ASPEED_PINCTRL_FUNC(SGPS1), + ASPEED_PINCTRL_FUNC(SGPS2), + ASPEED_PINCTRL_FUNC(SIOONCTRL), +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch new file mode 100644 index 000000000..3cd1d9e84 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch @@ -0,0 +1,4392 @@ +From ce7a88017fb2124100c4e5481a205034f34da23c Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 7 Jan 2019 09:56:10 -0800 +Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version + +Upstreaming is in holding. It's for adding DTS sensor with PECI +subsystem code update. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + Documentation/hwmon/peci-cputemp | 34 +- + drivers/hwmon/Kconfig | 4 +- + drivers/hwmon/peci-cputemp.c | 156 ++++-- + drivers/hwmon/peci-dimmtemp.c | 69 +-- + drivers/hwmon/peci-hwmon.h | 9 +- + drivers/mfd/Kconfig | 5 +- + drivers/mfd/intel-peci-client.c | 43 +- + drivers/peci/Kconfig | 35 +- + drivers/peci/Makefile | 6 +- + drivers/peci/busses/Kconfig | 19 + + drivers/peci/busses/Makefile | 6 + + drivers/peci/busses/peci-aspeed.c | 494 +++++++++++++++++++ + drivers/peci/peci-aspeed.c | 505 ------------------- + drivers/peci/peci-core.c | 889 ++++++++++++++++++---------------- + drivers/peci/peci-dev.c | 340 +++++++++++++ + include/linux/mfd/intel-peci-client.h | 6 +- + include/linux/peci.h | 30 +- + include/uapi/linux/peci-ioctl.h | 394 ++++++++------- + 18 files changed, 1805 insertions(+), 1239 deletions(-) + create mode 100644 drivers/peci/busses/Kconfig + create mode 100644 drivers/peci/busses/Makefile + create mode 100644 drivers/peci/busses/peci-aspeed.c + delete mode 100644 drivers/peci/peci-aspeed.c + create mode 100644 drivers/peci/peci-dev.c + +diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp +index 821a9258f2e6..a3a3e465c888 100644 +--- a/Documentation/hwmon/peci-cputemp ++++ b/Documentation/hwmon/peci-cputemp +@@ -51,28 +51,38 @@ temp1_crit Provides shutdown temperature of the CPU package which + temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of + the CPU package. + +-temp2_label "Tcontrol" +-temp2_input Provides current Tcontrol temperature of the CPU ++temp2_label "DTS" ++temp2_input Provides current DTS temperature of the CPU package. ++temp2_max Provides thermal control temperature of the CPU package ++ which is also known as Tcontrol. ++temp2_crit Provides shutdown temperature of the CPU package which ++ is also known as the maximum processor junction ++ temperature, Tjmax or Tprochot. ++temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of ++ the CPU package. ++ ++temp3_label "Tcontrol" ++temp3_input Provides current Tcontrol temperature of the CPU + package which is also known as Fan Temperature target. + Indicates the relative value from thermal monitor trip + temperature at which fans should be engaged. +-temp2_crit Provides Tcontrol critical value of the CPU package ++temp3_crit Provides Tcontrol critical value of the CPU package + which is same to Tjmax. + +-temp3_label "Tthrottle" +-temp3_input Provides current Tthrottle temperature of the CPU ++temp4_label "Tthrottle" ++temp4_input Provides current Tthrottle temperature of the CPU + package. Used for throttling temperature. If this value + is allowed and lower than Tjmax - the throttle will + occur and reported at lower than Tjmax. + +-temp4_label "Tjmax" +-temp4_input Provides the maximum junction temperature, Tjmax of the ++temp5_label "Tjmax" ++temp5_input Provides the maximum junction temperature, Tjmax of the + CPU package. + +-temp[5-*]_label Provides string "Core X", where X is resolved core ++temp[6-*]_label Provides string "Core X", where X is resolved core + number. +-temp[5-*]_input Provides current temperature of each core. +-temp[5-*]_max Provides thermal control temperature of the core. +-temp[5-*]_crit Provides shutdown temperature of the core. +-temp[5-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of ++temp[6-*]_input Provides current temperature of each core. ++temp[6-*]_max Provides thermal control temperature of the core. ++temp[6-*]_crit Provides shutdown temperature of the core. ++temp[6-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of + the core. +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 9e118d700b48..efe67f7faed3 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1319,7 +1319,7 @@ config SENSORS_PECI_CPUTEMP + the PECI Client Command Suite via the processor PECI client. + Check Documentation/hwmon/peci-cputemp for details. + +- This driver can also be built as a module. If so, the module ++ This driver can also be built as a module. If so, the module + will be called peci-cputemp. + + config SENSORS_PECI_DIMMTEMP +@@ -1333,7 +1333,7 @@ config SENSORS_PECI_DIMMTEMP + Suite via the processor PECI client. + Check Documentation/hwmon/peci-dimmtemp for details. + +- This driver can also be built as a module. If so, the module ++ This driver can also be built as a module. If so, the module + will be called peci-dimmtemp. + + source drivers/hwmon/pmbus/Kconfig +diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c +index 11880c86a854..30ba1638e358 100644 +--- a/drivers/hwmon/peci-cputemp.c ++++ b/drivers/hwmon/peci-cputemp.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/hwmon.h> + #include <linux/jiffies.h> +@@ -9,7 +9,7 @@ + #include <linux/platform_device.h> + #include "peci-hwmon.h" + +-#define DEFAULT_CHANNEL_NUMS 4 ++#define DEFAULT_CHANNEL_NUMS 5 + #define CORETEMP_CHANNEL_NUMS CORE_NUMS_MAX + #define CPUTEMP_CHANNEL_NUMS (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS) + +@@ -21,6 +21,7 @@ + + struct temp_group { + struct temp_data die; ++ struct temp_data dts; + struct temp_data tcontrol; + struct temp_data tthrottle; + struct temp_data tjmax; +@@ -43,6 +44,7 @@ struct peci_cputemp { + + enum cputemp_channels { + channel_die, ++ channel_dts, + channel_tcontrol, + channel_tthrottle, + channel_tjmax, +@@ -54,6 +56,10 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = { + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + ++ /* DTS margin */ ++ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | ++ HWMON_T_CRIT_HYST, ++ + /* Tcontrol temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, + +@@ -70,6 +76,7 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = { + + static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = { + "Die", ++ "DTS", + "Tcontrol", + "Tthrottle", + "Tjmax", +@@ -92,19 +99,20 @@ static int get_temp_targets(struct peci_cputemp *priv) + s32 tthrottle_offset; + s32 tcontrol_margin; + u8 pkg_cfg[4]; +- int rc; ++ int ret; + +- /** ++ /* + * Just use only the tcontrol marker to determine if target values need + * update. + */ + if (!peci_temp_need_update(&priv->temp.tcontrol)) + return 0; + +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_TEMP_TARGET, 0, pkg_cfg); +- if (rc) +- return rc; ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_TEMP_TARGET, 0, ++ pkg_cfg); ++ if (ret) ++ return ret; + + priv->temp.tjmax.value = pkg_cfg[2] * 1000; + +@@ -123,17 +131,16 @@ static int get_temp_targets(struct peci_cputemp *priv) + static int get_die_temp(struct peci_cputemp *priv) + { + struct peci_get_temp_msg msg; +- int rc; ++ int ret; + + if (!peci_temp_need_update(&priv->temp.die)) + return 0; + + msg.addr = priv->mgr->client->addr; + +- rc = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, +- &msg); +- if (rc) +- return rc; ++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg); ++ if (ret) ++ return ret; + + /* Note that the tjmax should be available before calling it */ + priv->temp.die.value = priv->temp.tjmax.value + +@@ -144,24 +151,67 @@ static int get_die_temp(struct peci_cputemp *priv) + return 0; + } + ++static int get_dts(struct peci_cputemp *priv) ++{ ++ struct peci_rd_pkg_cfg_msg msg; ++ s32 dts_margin; ++ int ret; ++ ++ if (!peci_temp_need_update(&priv->temp.dts)) ++ return 0; ++ ++ msg.addr = priv->mgr->client->addr; ++ msg.index = PECI_MBX_INDEX_DTS_MARGIN; ++ msg.param = 0; ++ msg.rx_len = 4; ++ ++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_RD_PKG_CFG, ++ &msg); ++ if (ret) ++ return ret; ++ ++ dts_margin = (msg.pkg_config[1] << 8) | msg.pkg_config[0]; ++ ++ /** ++ * Processors return a value of DTS reading in 10.6 format ++ * (10 bits signed decimal, 6 bits fractional). ++ * Error codes: ++ * 0x8000: General sensor error ++ * 0x8001: Reserved ++ * 0x8002: Underflow on reading value ++ * 0x8003-0x81ff: Reserved ++ */ ++ if (dts_margin >= 0x8000 && dts_margin <= 0x81ff) ++ return -EIO; ++ ++ dts_margin = ten_dot_six_to_millidegree(dts_margin); ++ ++ /* Note that the tcontrol should be available before calling it */ ++ priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin; ++ ++ peci_temp_mark_updated(&priv->temp.dts); ++ ++ return 0; ++} ++ + static int get_core_temp(struct peci_cputemp *priv, int core_index) + { + s32 core_dts_margin; + u8 pkg_cfg[4]; +- int rc; ++ int ret; + + if (!peci_temp_need_update(&priv->temp.core[core_index])) + return 0; + +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_PER_CORE_DTS_TEMP, +- core_index, pkg_cfg); +- if (rc) +- return rc; ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_PER_CORE_DTS_TEMP, ++ core_index, pkg_cfg); ++ if (ret) ++ return ret; + + core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg); + +- /** ++ /* + * Processors return a value of the core DTS reading in 10.6 format + * (10 bits signed decimal, 6 bits fractional). + * Error codes: +@@ -192,6 +242,7 @@ static int cputemp_read_string(struct device *dev, + return -EOPNOTSUPP; + + *str = cputemp_label[channel]; ++ + return 0; + } + +@@ -200,26 +251,33 @@ static int cputemp_read(struct device *dev, + u32 attr, int channel, long *val) + { + struct peci_cputemp *priv = dev_get_drvdata(dev); +- int rc, core_index; ++ int ret, core_index; + + if (channel >= CPUTEMP_CHANNEL_NUMS || + !(priv->temp_config[channel] & BIT(attr))) + return -EOPNOTSUPP; + +- rc = get_temp_targets(priv); +- if (rc) +- return rc; ++ ret = get_temp_targets(priv); ++ if (ret) ++ return ret; + + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case channel_die: +- rc = get_die_temp(priv); +- if (rc) ++ ret = get_die_temp(priv); ++ if (ret) + break; + + *val = priv->temp.die.value; + break; ++ case channel_dts: ++ ret = get_dts(priv); ++ if (ret) ++ break; ++ ++ *val = priv->temp.dts.value; ++ break; + case channel_tcontrol: + *val = priv->temp.tcontrol.value; + break; +@@ -231,8 +289,8 @@ static int cputemp_read(struct device *dev, + break; + default: + core_index = channel - DEFAULT_CHANNEL_NUMS; +- rc = get_core_temp(priv, core_index); +- if (rc) ++ ret = get_core_temp(priv, core_index); ++ if (ret) + break; + + *val = priv->temp.core[core_index].value; +@@ -249,11 +307,11 @@ static int cputemp_read(struct device *dev, + *val = priv->temp.tjmax.value - priv->temp.tcontrol.value; + break; + default: +- rc = -EOPNOTSUPP; ++ ret = -EOPNOTSUPP; + break; + } + +- return rc; ++ return ret; + } + + static umode_t cputemp_is_visible(const void *data, +@@ -262,11 +320,11 @@ static umode_t cputemp_is_visible(const void *data, + { + const struct peci_cputemp *priv = data; + +- if (priv->temp_config[channel] & BIT(attr)) +- if (channel < DEFAULT_CHANNEL_NUMS || +- (channel >= DEFAULT_CHANNEL_NUMS && +- (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))) +- return 0444; ++ if ((priv->temp_config[channel] & BIT(attr)) && ++ (channel < DEFAULT_CHANNEL_NUMS || ++ (channel >= DEFAULT_CHANNEL_NUMS && ++ (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS))))) ++ return 0444; + + return 0; + } +@@ -280,7 +338,7 @@ static const struct hwmon_ops cputemp_ops = { + static int check_resolved_cores(struct peci_cputemp *priv) + { + struct peci_rd_pci_cfg_local_msg msg; +- int rc; ++ int ret; + + /* Get the RESOLVED_CORES register value */ + msg.addr = priv->mgr->client->addr; +@@ -290,30 +348,31 @@ static int check_resolved_cores(struct peci_cputemp *priv) + msg.reg = REG_RESOLVED_CORES_OFFSET; + msg.rx_len = 4; + +- rc = peci_command(priv->mgr->client->adapter, +- PECI_CMD_RD_PCI_CFG_LOCAL, &msg); +- if (rc) +- return rc; ++ ret = peci_command(priv->mgr->client->adapter, ++ PECI_CMD_RD_PCI_CFG_LOCAL, &msg); ++ if (ret) ++ return ret; + + priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config); + if (!priv->core_mask) + return -EAGAIN; + + dev_dbg(priv->dev, "Scanned resolved cores: 0x%x\n", priv->core_mask); ++ + return 0; + } + + static int create_core_temp_info(struct peci_cputemp *priv) + { +- int rc, i; ++ int ret, i; + +- rc = check_resolved_cores(priv); +- if (rc) +- return rc; ++ ret = check_resolved_cores(priv); ++ if (ret) ++ return ret; + + for (i = 0; i < priv->gen_info->core_max; i++) + if (priv->core_mask & BIT(i)) +- while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx) ++ while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS) + priv->temp_config[priv->config_idx++] = + config_table[channel_core]; + +@@ -326,7 +385,7 @@ static int peci_cputemp_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + struct peci_cputemp *priv; + struct device *hwmon_dev; +- int rc; ++ int ret; + + if ((mgr->client->adapter->cmd_mask & + (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) != +@@ -346,12 +405,13 @@ static int peci_cputemp_probe(struct platform_device *pdev) + mgr->client->addr - PECI_BASE_ADDR); + + priv->temp_config[priv->config_idx++] = config_table[channel_die]; ++ priv->temp_config[priv->config_idx++] = config_table[channel_dts]; + priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol]; + priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle]; + priv->temp_config[priv->config_idx++] = config_table[channel_tjmax]; + +- rc = create_core_temp_info(priv); +- if (rc) ++ ret = create_core_temp_info(priv); ++ if (ret) + dev_dbg(dev, "Skipped creating core temp info\n"); + + priv->chip.ops = &cputemp_ops; +diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c +index 86a45a90805b..e088366fd138 100644 +--- a/drivers/hwmon/peci-dimmtemp.c ++++ b/drivers/hwmon/peci-dimmtemp.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/hwmon.h> + #include <linux/jiffies.h> +@@ -45,16 +45,16 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no) + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + u8 cfg_data[4]; +- int rc; ++ int ret; + + if (!peci_temp_need_update(&priv->temp[dimm_no])) + return 0; + +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_DDR_DIMM_TEMP, +- chan_rank, cfg_data); +- if (rc) +- return rc; ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_DDR_DIMM_TEMP, ++ chan_rank, cfg_data); ++ if (ret) ++ return ret; + + priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000; + +@@ -77,6 +77,7 @@ static int dimmtemp_read_string(struct device *dev, + chan_rank = channel / dimm_idx_max; + dimm_idx = channel % dimm_idx_max; + *str = dimmtemp_label[chan_rank][dimm_idx]; ++ + return 0; + } + +@@ -84,16 +85,17 @@ static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) + { + struct peci_dimmtemp *priv = dev_get_drvdata(dev); +- int rc; ++ int ret; + + if (attr != hwmon_temp_input) + return -EOPNOTSUPP; + +- rc = get_dimm_temp(priv, channel); +- if (rc) +- return rc; ++ ret = get_dimm_temp(priv, channel); ++ if (ret) ++ return ret; + + *val = priv->temp[channel].value; ++ + return 0; + } + +@@ -120,16 +122,16 @@ static int check_populated_dimms(struct peci_dimmtemp *priv) + { + u32 chan_rank_max = priv->gen_info->chan_rank_max; + u32 dimm_idx_max = priv->gen_info->dimm_idx_max; +- int chan_rank, dimm_idx, rc; ++ int chan_rank, dimm_idx, ret; + u8 cfg_data[4]; + + for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_DDR_DIMM_TEMP, +- chan_rank, cfg_data); +- if (rc) { ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_DDR_DIMM_TEMP, ++ chan_rank, cfg_data); ++ if (ret) { + priv->dimm_mask = 0; +- return rc; ++ return ret; + } + + for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) +@@ -143,17 +145,18 @@ static int check_populated_dimms(struct peci_dimmtemp *priv) + return -EAGAIN; + + dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask); ++ + return 0; + } + + static int create_dimm_temp_info(struct peci_dimmtemp *priv) + { +- int rc, i, config_idx, channels; ++ int ret, i, config_idx, channels; + struct device *hwmon_dev; + +- rc = check_populated_dimms(priv); +- if (rc) { +- if (rc == -EAGAIN) { ++ ret = check_populated_dimms(priv); ++ if (ret) { ++ if (ret == -EAGAIN) { + if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) { + queue_delayed_work(priv->work_queue, + &priv->work_handler, +@@ -164,11 +167,11 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv) + } else { + dev_err(priv->dev, + "Timeout DIMM temp info creation\n"); +- rc = -ETIMEDOUT; ++ ret = -ETIMEDOUT; + } + } + +- return rc; ++ return ret; + } + + channels = priv->gen_info->chan_rank_max * +@@ -192,12 +195,12 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv) + priv, + &priv->chip, + NULL); +- rc = PTR_ERR_OR_ZERO(hwmon_dev); +- if (!rc) ++ ret = PTR_ERR_OR_ZERO(hwmon_dev); ++ if (!ret) + dev_dbg(priv->dev, "%s: sensor '%s'\n", + dev_name(hwmon_dev), priv->name); + +- return rc; ++ return ret; + } + + static void create_dimm_temp_info_delayed(struct work_struct *work) +@@ -205,10 +208,10 @@ static void create_dimm_temp_info_delayed(struct work_struct *work) + struct delayed_work *dwork = to_delayed_work(work); + struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp, + work_handler); +- int rc; ++ int ret; + +- rc = create_dimm_temp_info(priv); +- if (rc && rc != -EAGAIN) ++ ret = create_dimm_temp_info(priv); ++ if (ret && ret != -EAGAIN) + dev_dbg(priv->dev, "Failed to create DIMM temp info\n"); + } + +@@ -217,7 +220,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev) + struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct peci_dimmtemp *priv; +- int rc; ++ int ret; + + if ((mgr->client->adapter->cmd_mask & + (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) != +@@ -242,8 +245,8 @@ static int peci_dimmtemp_probe(struct platform_device *pdev) + + INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed); + +- rc = create_dimm_temp_info(priv); +- if (rc && rc != -EAGAIN) { ++ ret = create_dimm_temp_info(priv); ++ if (ret && ret != -EAGAIN) { + dev_err(dev, "Failed to create DIMM temp info\n"); + goto err_free_wq; + } +@@ -252,7 +255,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev) + + err_free_wq: + destroy_workqueue(priv->work_queue); +- return rc; ++ return ret; + } + + static int peci_dimmtemp_remove(struct platform_device *pdev) +diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h +index 6ca1855a86bb..ce6b470eae63 100644 +--- a/drivers/hwmon/peci-hwmon.h ++++ b/drivers/hwmon/peci-hwmon.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __PECI_HWMON_H + #define __PECI_HWMON_H +@@ -29,11 +29,8 @@ struct temp_data { + */ + static inline bool peci_temp_need_update(struct temp_data *temp) + { +- if (temp->valid && +- time_before(jiffies, temp->last_updated + UPDATE_INTERVAL)) +- return false; +- +- return true; ++ return !temp->valid || ++ time_after(jiffies, temp->last_updated + UPDATE_INTERVAL); + } + + /** +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 66b71a6122d6..28a83b354ea8 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -596,7 +596,7 @@ config MFD_INTEL_MSIC + devices used in Intel Medfield platforms. + + config MFD_INTEL_PECI_CLIENT +- bool "Intel PECI client" ++ tristate "Intel PECI client" + depends on (PECI || COMPILE_TEST) + select MFD_CORE + help +@@ -609,6 +609,9 @@ config MFD_INTEL_PECI_CLIENT + Additional drivers must be enabled in order to use the functionality + of the device. + ++ This driver can also be built as a module. If so, the module ++ will be called intel-peci-client. ++ + config MFD_IPAQ_MICRO + bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" + depends on SA1100_H3100 || SA1100_H3600 +diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c +index d53e4f1078ac..d62442438512 100644 +--- a/drivers/mfd/intel-peci-client.c ++++ b/drivers/mfd/intel-peci-client.c +@@ -1,12 +1,12 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/mfd/core.h> + #include <linux/mfd/intel-peci-client.h> + #include <linux/module.h> +-#include <linux/peci.h> + #include <linux/of_device.h> ++#include <linux/peci.h> + + #define CPU_ID_MODEL_MASK GENMASK(7, 4) + #define CPU_ID_FAMILY_MASK GENMASK(11, 8) +@@ -18,12 +18,6 @@ + #define LOWER_BYTE_MASK GENMASK(7, 0) + #define UPPER_BYTE_MASK GENMASK(16, 8) + +-enum cpu_gens { +- CPU_GEN_HSX = 0, /* Haswell Xeon */ +- CPU_GEN_BRX, /* Broadwell Xeon */ +- CPU_GEN_SKX, /* Skylake Xeon */ +-}; +- + static struct mfd_cell peci_functions[] = { + { .name = "peci-cputemp", }, + { .name = "peci-dimmtemp", }, +@@ -31,19 +25,19 @@ static struct mfd_cell peci_functions[] = { + }; + + static const struct cpu_gen_info cpu_gen_info_table[] = { +- [CPU_GEN_HSX] = { ++ { /* Haswell Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_HASWELL_X, + .core_max = CORE_MAX_ON_HSX, + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX }, +- [CPU_GEN_BRX] = { ++ { /* Broadwell Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_BROADWELL_X, + .core_max = CORE_MAX_ON_BDX, + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX }, +- [CPU_GEN_SKX] = { ++ { /* Skylake Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_SKYLAKE_X, + .core_max = CORE_MAX_ON_SKX, +@@ -53,16 +47,17 @@ static const struct cpu_gen_info cpu_gen_info_table[] = { + + static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) + { ++ struct device *dev = &priv->client->dev; + u32 cpu_id; + u16 family; + u8 model; +- int rc; ++ int ret; + int i; + +- rc = peci_get_cpu_id(priv->client->adapter, priv->client->addr, +- &cpu_id); +- if (rc) +- return rc; ++ ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr, ++ &cpu_id); ++ if (ret) ++ return ret; + + family = FIELD_PREP(LOWER_BYTE_MASK, + FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) | +@@ -83,11 +78,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) + } + + if (!priv->gen_info) { +- dev_err(priv->dev, "Can't support this CPU: 0x%x\n", cpu_id); +- rc = -ENODEV; ++ dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id); ++ ret = -ENODEV; + } + +- return rc; ++ return ret; + } + + static int peci_client_probe(struct peci_client *client) +@@ -103,31 +98,29 @@ static int peci_client_probe(struct peci_client *client) + + dev_set_drvdata(dev, priv); + priv->client = client; +- priv->dev = dev; + cpu_no = client->addr - PECI_BASE_ADDR; + + ret = peci_client_get_cpu_gen_info(priv); + if (ret) + return ret; + +- ret = devm_mfd_add_devices(priv->dev, cpu_no, peci_functions, ++ ret = devm_mfd_add_devices(dev, cpu_no, peci_functions, + ARRAY_SIZE(peci_functions), NULL, 0, NULL); + if (ret < 0) { +- dev_err(priv->dev, "Failed to register child devices: %d\n", +- ret); ++ dev_err(dev, "Failed to register child devices: %d\n", ret); + return ret; + } + + return 0; + } + +-#ifdef CONFIG_OF ++#if IS_ENABLED(CONFIG_OF) + static const struct of_device_id peci_client_of_table[] = { + { .compatible = "intel,peci-client" }, + { } + }; + MODULE_DEVICE_TABLE(of, peci_client_of_table); +-#endif ++#endif /* CONFIG_OF */ + + static const struct peci_device_id peci_client_ids[] = { + { .name = "peci-client" }, +diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig +index 9e9845ebcff4..9752feee2454 100644 +--- a/drivers/peci/Kconfig ++++ b/drivers/peci/Kconfig +@@ -2,10 +2,12 @@ + # Platform Environment Control Interface (PECI) subsystem configuration + # + ++menu "PECI support" ++ + config PECI +- bool "PECI support" +- select RT_MUTEXES ++ tristate "PECI support" + select CRC8 ++ default n + help + The Platform Environment Control Interface (PECI) is a one-wire bus + interface that provides a communication channel from Intel processors +@@ -14,26 +16,23 @@ config PECI + If you want PECI support, you should say Y here and also to the + specific driver for your bus adapter(s) below. + +-if PECI +- +-# +-# PECI hardware bus configuration +-# ++ This support is also available as a module. If so, the module ++ will be called peci-core. + +-menu "PECI Hardware Bus support" ++if PECI + +-config PECI_ASPEED +- tristate "ASPEED PECI support" +- select REGMAP_MMIO +- depends on OF +- depends on ARCH_ASPEED || COMPILE_TEST ++config PECI_CHARDEV ++ tristate "PECI device interface" + help +- Say Y here if you want support for the Platform Environment Control +- Interface (PECI) bus adapter driver on the ASPEED SoCs. ++ Say Y here to use peci-* device files, usually found in the /dev ++ directory on your system. They make it possible to have user-space ++ programs use the PECI bus. + +- This support is also available as a module. If so, the module +- will be called peci-aspeed. ++ This support is also available as a module. If so, the module ++ will be called peci-dev. + +-endmenu ++source "drivers/peci/busses/Kconfig" + + endif # PECI ++ ++endmenu +diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile +index 886285e69765..da8b0a33fa42 100644 +--- a/drivers/peci/Makefile ++++ b/drivers/peci/Makefile +@@ -1,9 +1,11 @@ ++# SPDX-License-Identifier: GPL-2.0 + # +-# Makefile for the PECI core and bus drivers. ++# Makefile for the PECI core drivers. + # + + # Core functionality + obj-$(CONFIG_PECI) += peci-core.o ++obj-$(CONFIG_PECI_CHARDEV) += peci-dev.o + + # Hardware specific bus drivers +-obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o ++obj-y += busses/ +diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig +new file mode 100644 +index 000000000000..a20d470b4250 +--- /dev/null ++++ b/drivers/peci/busses/Kconfig +@@ -0,0 +1,19 @@ ++# ++# PECI hardware bus configuration ++# ++ ++menu "PECI Hardware Bus support" ++ ++config PECI_ASPEED ++ tristate "ASPEED PECI support" ++ depends on ARCH_ASPEED || COMPILE_TEST ++ depends on OF ++ depends on PECI ++ help ++ Say Y here if you want support for the Platform Environment Control ++ Interface (PECI) bus adapter driver on the ASPEED SoCs. ++ ++ This support is also available as a module. If so, the module ++ will be called peci-aspeed. ++ ++endmenu +diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile +new file mode 100644 +index 000000000000..69e31dfaca19 +--- /dev/null ++++ b/drivers/peci/busses/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Makefile for the PECI hardware bus drivers. ++# ++ ++obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o +diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c +new file mode 100644 +index 000000000000..8a0dd40730cc +--- /dev/null ++++ b/drivers/peci/busses/peci-aspeed.c +@@ -0,0 +1,494 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2018-2019 Intel Corporation ++ ++#include <linux/bitfield.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/peci.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++ ++/* ASPEED PECI Registers */ ++/* Control Register */ ++#define ASPEED_PECI_CTRL 0x00 ++#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) ++#define ASPEED_PECI_CTRL_READ_MODE_MASK GENMASK(13, 12) ++#define ASPEED_PECI_CTRL_READ_MODE_COUNT BIT(12) ++#define ASPEED_PECI_CTRL_READ_MODE_DBG BIT(13) ++#define ASPEED_PECI_CTRL_CLK_SOURCE_MASK BIT(11) ++#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) ++#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7) ++#define ASPEED_PECI_CTRL_INVERT_IN BIT(6) ++#define ASPEED_PECI_CTRL_BUS_CONTENT_EN BIT(5) ++#define ASPEED_PECI_CTRL_PECI_EN BIT(4) ++#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0) ++ ++/* Timing Negotiation Register */ ++#define ASPEED_PECI_TIMING_NEGOTIATION 0x04 ++#define ASPEED_PECI_TIMING_MESSAGE_MASK GENMASK(15, 8) ++#define ASPEED_PECI_TIMING_ADDRESS_MASK GENMASK(7, 0) ++ ++/* Command Register */ ++#define ASPEED_PECI_CMD 0x08 ++#define ASPEED_PECI_CMD_PIN_MON BIT(31) ++#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24) ++#define ASPEED_PECI_CMD_IDLE_MASK (ASPEED_PECI_CMD_STS_MASK | \ ++ ASPEED_PECI_CMD_PIN_MON) ++#define ASPEED_PECI_CMD_FIRE BIT(0) ++ ++/* Read/Write Length Register */ ++#define ASPEED_PECI_RW_LENGTH 0x0c ++#define ASPEED_PECI_AW_FCS_EN BIT(31) ++#define ASPEED_PECI_READ_LEN_MASK GENMASK(23, 16) ++#define ASPEED_PECI_WRITE_LEN_MASK GENMASK(15, 8) ++#define ASPEED_PECI_TAGET_ADDR_MASK GENMASK(7, 0) ++ ++/* Expected FCS Data Register */ ++#define ASPEED_PECI_EXP_FCS 0x10 ++#define ASPEED_PECI_EXP_READ_FCS_MASK GENMASK(23, 16) ++#define ASPEED_PECI_EXP_AW_FCS_AUTO_MASK GENMASK(15, 8) ++#define ASPEED_PECI_EXP_WRITE_FCS_MASK GENMASK(7, 0) ++ ++/* Captured FCS Data Register */ ++#define ASPEED_PECI_CAP_FCS 0x14 ++#define ASPEED_PECI_CAP_READ_FCS_MASK GENMASK(23, 16) ++#define ASPEED_PECI_CAP_WRITE_FCS_MASK GENMASK(7, 0) ++ ++/* Interrupt Register */ ++#define ASPEED_PECI_INT_CTRL 0x18 ++#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30) ++#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0 ++#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1 ++#define ASPEED_PECI_MESSAGE_NEGO 2 ++#define ASPEED_PECI_INT_MASK GENMASK(4, 0) ++#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4) ++#define ASPEED_PECI_INT_BUS_CONNECT BIT(3) ++#define ASPEED_PECI_INT_W_FCS_BAD BIT(2) ++#define ASPEED_PECI_INT_W_FCS_ABORT BIT(1) ++#define ASPEED_PECI_INT_CMD_DONE BIT(0) ++ ++/* Interrupt Status Register */ ++#define ASPEED_PECI_INT_STS 0x1c ++#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16) ++ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */ ++ ++/* Rx/Tx Data Buffer Registers */ ++#define ASPEED_PECI_W_DATA0 0x20 ++#define ASPEED_PECI_W_DATA1 0x24 ++#define ASPEED_PECI_W_DATA2 0x28 ++#define ASPEED_PECI_W_DATA3 0x2c ++#define ASPEED_PECI_R_DATA0 0x30 ++#define ASPEED_PECI_R_DATA1 0x34 ++#define ASPEED_PECI_R_DATA2 0x38 ++#define ASPEED_PECI_R_DATA3 0x3c ++#define ASPEED_PECI_W_DATA4 0x40 ++#define ASPEED_PECI_W_DATA5 0x44 ++#define ASPEED_PECI_W_DATA6 0x48 ++#define ASPEED_PECI_W_DATA7 0x4c ++#define ASPEED_PECI_R_DATA4 0x50 ++#define ASPEED_PECI_R_DATA5 0x54 ++#define ASPEED_PECI_R_DATA6 0x58 ++#define ASPEED_PECI_R_DATA7 0x5c ++#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32 ++ ++/* Timing Negotiation */ ++#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8 ++#define ASPEED_PECI_RD_SAMPLING_POINT_MAX 15 ++#define ASPEED_PECI_CLK_DIV_DEFAULT 0 ++#define ASPEED_PECI_CLK_DIV_MAX 7 ++#define ASPEED_PECI_MSG_TIMING_DEFAULT 1 ++#define ASPEED_PECI_MSG_TIMING_MAX 255 ++#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1 ++#define ASPEED_PECI_ADDR_TIMING_MAX 255 ++ ++/* Timeout */ ++#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC 50000 ++#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC 10000 ++#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000 ++#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 60000 ++ ++struct aspeed_peci { ++ struct peci_adapter *adapter; ++ struct device *dev; ++ void __iomem *base; ++ struct clk *clk; ++ struct reset_control *rst; ++ int irq; ++ spinlock_t lock; /* to sync completion status handling */ ++ struct completion xfer_complete; ++ u32 status; ++ u32 cmd_timeout_ms; ++}; ++ ++static int aspeed_peci_check_idle(struct aspeed_peci *priv) ++{ ++ ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC); ++ u32 cmd_sts; ++ ++ for (;;) { ++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD); ++ if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK)) ++ break; ++ if (time_after(jiffies, timeout)) { ++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD); ++ break; ++ } ++ usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1, ++ ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC); ++ } ++ ++ return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT; ++} ++ ++static int aspeed_peci_xfer(struct peci_adapter *adapter, ++ struct peci_xfer_msg *msg) ++{ ++ struct aspeed_peci *priv = peci_get_adapdata(adapter); ++ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); ++ u32 peci_head, peci_state, rx_data = 0; ++ ulong flags; ++ int i, ret; ++ uint reg; ++ ++ if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX || ++ msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX) ++ return -EINVAL; ++ ++ /* Check command sts and bus idle state */ ++ ret = aspeed_peci_check_idle(priv); ++ if (ret) ++ return ret; /* -ETIMEDOUT */ ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ reinit_completion(&priv->xfer_complete); ++ ++ peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) | ++ FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) | ++ FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len); ++ ++ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); ++ ++ for (i = 0; i < msg->tx_len; i += 4) { ++ reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 : ++ ASPEED_PECI_W_DATA4 + i % 16; ++ writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]), ++ priv->base + reg); ++ } ++ ++ dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); ++ print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1, ++ msg->tx_buf, msg->tx_len, true); ++ ++ priv->status = 0; ++ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete, ++ timeout); ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status); ++ peci_state = readl(priv->base + ASPEED_PECI_CMD); ++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", ++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state)); ++ ++ writel(0, priv->base + ASPEED_PECI_CMD); ++ ++ if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) { ++ if (err < 0) { /* -ERESTARTSYS */ ++ ret = (int)err; ++ goto err_irqrestore; ++ } else if (err == 0) { ++ dev_dbg(priv->dev, "Timeout waiting for a response!\n"); ++ ret = -ETIMEDOUT; ++ goto err_irqrestore; ++ } ++ ++ dev_dbg(priv->dev, "No valid response!\n"); ++ ret = -EIO; ++ goto err_irqrestore; ++ } ++ ++ /* ++ * Note that rx_len and rx_buf size can be an odd number. ++ * Byte handling is more efficient. ++ */ ++ for (i = 0; i < msg->rx_len; i++) { ++ u8 byte_offset = i % 4; ++ ++ if (byte_offset == 0) { ++ reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 : ++ ASPEED_PECI_R_DATA4 + i % 16; ++ rx_data = readl(priv->base + reg); ++ } ++ ++ msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3)); ++ } ++ ++ print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1, ++ msg->rx_buf, msg->rx_len, true); ++ ++ peci_state = readl(priv->base + ASPEED_PECI_CMD); ++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", ++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state)); ++ dev_dbg(priv->dev, "------------------------\n"); ++ ++err_irqrestore: ++ spin_unlock_irqrestore(&priv->lock, flags); ++ return ret; ++} ++ ++static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) ++{ ++ struct aspeed_peci *priv = arg; ++ u32 status; ++ ++ spin_lock(&priv->lock); ++ status = readl(priv->base + ASPEED_PECI_INT_STS); ++ writel(status, priv->base + ASPEED_PECI_INT_STS); ++ priv->status |= (status & ASPEED_PECI_INT_MASK); ++ ++ /* ++ * In most cases, interrupt bits will be set one by one but also note ++ * that multiple interrupt bits could be set at the same time. ++ */ ++ if (status & ASPEED_PECI_INT_BUS_TIMEOUT) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n"); ++ } ++ ++ if (status & ASPEED_PECI_INT_BUS_CONNECT) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n"); ++ } ++ ++ if (status & ASPEED_PECI_INT_W_FCS_BAD) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n"); ++ } ++ ++ if (status & ASPEED_PECI_INT_W_FCS_ABORT) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n"); ++ } ++ ++ /* ++ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit ++ * set even in an error case. ++ */ ++ if (status & ASPEED_PECI_INT_CMD_DONE) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n"); ++ complete(&priv->xfer_complete); ++ } ++ ++ spin_unlock(&priv->lock); ++ return IRQ_HANDLED; ++} ++ ++static int aspeed_peci_init_ctrl(struct aspeed_peci *priv) ++{ ++ u32 msg_timing, addr_timing, rd_sampling_point; ++ u32 clk_freq, clk_divisor, clk_div_val = 0; ++ int ret; ++ ++ priv->clk = devm_clk_get(priv->dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ dev_err(priv->dev, "Failed to get clk source.\n"); ++ return PTR_ERR(priv->clk); ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret) { ++ dev_err(priv->dev, "Failed to enable clock.\n"); ++ return ret; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "clock-frequency", &clk_freq); ++ if (ret) { ++ dev_err(priv->dev, ++ "Could not read clock-frequency property.\n"); ++ clk_disable_unprepare(priv->clk); ++ return ret; ++ } ++ ++ clk_divisor = clk_get_rate(priv->clk) / clk_freq; ++ ++ while ((clk_divisor >> 1) && (clk_div_val < ASPEED_PECI_CLK_DIV_MAX)) ++ clk_div_val++; ++ ++ ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing); ++ if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid msg-timing : %u, Use default : %u\n", ++ msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT); ++ msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing); ++ if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid addr-timing : %u, Use default : %u\n", ++ addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT); ++ addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "rd-sampling-point", ++ &rd_sampling_point); ++ if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid rd-sampling-point : %u. Use default : %u\n", ++ rd_sampling_point, ++ ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT); ++ rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "cmd-timeout-ms", ++ &priv->cmd_timeout_ms); ++ if (ret || priv->cmd_timeout_ms > ASPEED_PECI_CMD_TIMEOUT_MS_MAX || ++ priv->cmd_timeout_ms == 0) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid cmd-timeout-ms : %u. Use default : %u\n", ++ priv->cmd_timeout_ms, ++ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT); ++ priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT; ++ } ++ ++ writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, ++ ASPEED_PECI_CLK_DIV_DEFAULT) | ++ ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL); ++ ++ /* ++ * Timing negotiation period setting. ++ * The unit of the programmed value is 4 times of PECI clock period. ++ */ ++ writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) | ++ FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing), ++ priv->base + ASPEED_PECI_TIMING_NEGOTIATION); ++ ++ /* Clear interrupts */ ++ writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK, ++ priv->base + ASPEED_PECI_INT_STS); ++ ++ /* Set timing negotiation mode and enable interrupts */ ++ writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ++ ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) | ++ ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL); ++ ++ /* Read sampling point and clock speed setting */ ++ writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) | ++ FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) | ++ ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN, ++ priv->base + ASPEED_PECI_CTRL); ++ ++ return 0; ++} ++ ++static int aspeed_peci_probe(struct platform_device *pdev) ++{ ++ struct peci_adapter *adapter; ++ struct aspeed_peci *priv; ++ struct resource *res; ++ int ret; ++ ++ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv)); ++ if (!adapter) ++ return -ENOMEM; ++ ++ priv = peci_get_adapdata(adapter); ++ priv->adapter = adapter; ++ priv->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) { ++ ret = PTR_ERR(priv->base); ++ goto err_put_adapter_dev; ++ } ++ ++ priv->irq = platform_get_irq(pdev, 0); ++ if (!priv->irq) { ++ ret = -ENODEV; ++ goto err_put_adapter_dev; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, ++ 0, "peci-aspeed-irq", priv); ++ if (ret) ++ goto err_put_adapter_dev; ++ ++ init_completion(&priv->xfer_complete); ++ spin_lock_init(&priv->lock); ++ ++ priv->adapter->owner = THIS_MODULE; ++ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev)); ++ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name)); ++ priv->adapter->xfer = aspeed_peci_xfer; ++ priv->adapter->use_dma = false; ++ ++ priv->rst = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->rst)) { ++ dev_err(&pdev->dev, ++ "missing or invalid reset controller entry"); ++ ret = PTR_ERR(priv->rst); ++ goto err_put_adapter_dev; ++ } ++ reset_control_deassert(priv->rst); ++ ++ ret = aspeed_peci_init_ctrl(priv); ++ if (ret) ++ goto err_put_adapter_dev; ++ ++ ret = peci_add_adapter(priv->adapter); ++ if (ret) ++ goto err_put_adapter_dev; ++ ++ dev_info(&pdev->dev, "peci bus %d registered, irq %d\n", ++ priv->adapter->nr, priv->irq); ++ ++ return 0; ++ ++err_put_adapter_dev: ++ put_device(&adapter->dev); ++ return ret; ++} ++ ++static int aspeed_peci_remove(struct platform_device *pdev) ++{ ++ struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev); ++ ++ clk_disable_unprepare(priv->clk); ++ reset_control_assert(priv->rst); ++ peci_del_adapter(priv->adapter); ++ of_node_put(priv->adapter->dev.of_node); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_peci_of_table[] = { ++ { .compatible = "aspeed,ast2400-peci", }, ++ { .compatible = "aspeed,ast2500-peci", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); ++ ++static struct platform_driver aspeed_peci_driver = { ++ .probe = aspeed_peci_probe, ++ .remove = aspeed_peci_remove, ++ .driver = { ++ .name = "peci-aspeed", ++ .of_match_table = of_match_ptr(aspeed_peci_of_table), ++ }, ++}; ++module_platform_driver(aspeed_peci_driver); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED PECI driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c +deleted file mode 100644 +index 51cb2563ceb6..000000000000 +--- a/drivers/peci/peci-aspeed.c ++++ /dev/null +@@ -1,505 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-// Copyright (C) 2012-2017 ASPEED Technology Inc. +-// Copyright (c) 2018 Intel Corporation +- +-#include <linux/bitfield.h> +-#include <linux/clk.h> +-#include <linux/interrupt.h> +-#include <linux/jiffies.h> +-#include <linux/module.h> +-#include <linux/of.h> +-#include <linux/peci.h> +-#include <linux/platform_device.h> +-#include <linux/regmap.h> +-#include <linux/reset.h> +- +-/* ASPEED PECI Registers */ +-#define ASPEED_PECI_CTRL 0x00 +-#define ASPEED_PECI_TIMING 0x04 +-#define ASPEED_PECI_CMD 0x08 +-#define ASPEED_PECI_CMD_CTRL 0x0c +-#define ASPEED_PECI_EXP_FCS 0x10 +-#define ASPEED_PECI_CAP_FCS 0x14 +-#define ASPEED_PECI_INT_CTRL 0x18 +-#define ASPEED_PECI_INT_STS 0x1c +-#define ASPEED_PECI_W_DATA0 0x20 +-#define ASPEED_PECI_W_DATA1 0x24 +-#define ASPEED_PECI_W_DATA2 0x28 +-#define ASPEED_PECI_W_DATA3 0x2c +-#define ASPEED_PECI_R_DATA0 0x30 +-#define ASPEED_PECI_R_DATA1 0x34 +-#define ASPEED_PECI_R_DATA2 0x38 +-#define ASPEED_PECI_R_DATA3 0x3c +-#define ASPEED_PECI_W_DATA4 0x40 +-#define ASPEED_PECI_W_DATA5 0x44 +-#define ASPEED_PECI_W_DATA6 0x48 +-#define ASPEED_PECI_W_DATA7 0x4c +-#define ASPEED_PECI_R_DATA4 0x50 +-#define ASPEED_PECI_R_DATA5 0x54 +-#define ASPEED_PECI_R_DATA6 0x58 +-#define ASPEED_PECI_R_DATA7 0x5c +- +-/* ASPEED_PECI_CTRL - 0x00 : Control Register */ +-#define PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) +-#define PECI_CTRL_READ_MODE_MASK GENMASK(13, 12) +-#define PECI_CTRL_READ_MODE_COUNT BIT(12) +-#define PECI_CTRL_READ_MODE_DBG BIT(13) +-#define PECI_CTRL_CLK_SOURCE_MASK BIT(11) +-#define PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) +-#define PECI_CTRL_INVERT_OUT BIT(7) +-#define PECI_CTRL_INVERT_IN BIT(6) +-#define PECI_CTRL_BUS_CONTENT_EN BIT(5) +-#define PECI_CTRL_PECI_EN BIT(4) +-#define PECI_CTRL_PECI_CLK_EN BIT(0) +- +-/* ASPEED_PECI_TIMING - 0x04 : Timing Negotiation Register */ +-#define PECI_TIMING_MESSAGE_MASK GENMASK(15, 8) +-#define PECI_TIMING_ADDRESS_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_CMD - 0x08 : Command Register */ +-#define PECI_CMD_PIN_MON BIT(31) +-#define PECI_CMD_STS_MASK GENMASK(27, 24) +-#define PECI_CMD_IDLE_MASK (PECI_CMD_STS_MASK | PECI_CMD_PIN_MON) +-#define PECI_CMD_FIRE BIT(0) +- +-/* ASPEED_PECI_LEN - 0x0C : Read/Write Length Register */ +-#define PECI_AW_FCS_EN BIT(31) +-#define PECI_READ_LEN_MASK GENMASK(23, 16) +-#define PECI_WRITE_LEN_MASK GENMASK(15, 8) +-#define PECI_TAGET_ADDR_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_EXP_FCS - 0x10 : Expected FCS Data Register */ +-#define PECI_EXPECT_READ_FCS_MASK GENMASK(23, 16) +-#define PECI_EXPECT_AW_FCS_AUTO_MASK GENMASK(15, 8) +-#define PECI_EXPECT_WRITE_FCS_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_CAP_FCS - 0x14 : Captured FCS Data Register */ +-#define PECI_CAPTURE_READ_FCS_MASK GENMASK(23, 16) +-#define PECI_CAPTURE_WRITE_FCS_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_INT_CTRL/STS - 0x18/0x1c : Interrupt Register */ +-#define PECI_INT_TIMING_RESULT_MASK GENMASK(31, 30) +-#define PECI_INT_TIMEOUT BIT(4) +-#define PECI_INT_CONNECT BIT(3) +-#define PECI_INT_W_FCS_BAD BIT(2) +-#define PECI_INT_W_FCS_ABORT BIT(1) +-#define PECI_INT_CMD_DONE BIT(0) +- +-#define PECI_INT_MASK (PECI_INT_TIMEOUT | PECI_INT_CONNECT | \ +- PECI_INT_W_FCS_BAD | PECI_INT_W_FCS_ABORT | \ +- PECI_INT_CMD_DONE) +- +-#define PECI_IDLE_CHECK_TIMEOUT_USEC 50000 +-#define PECI_IDLE_CHECK_INTERVAL_USEC 10000 +- +-#define PECI_RD_SAMPLING_POINT_DEFAULT 8 +-#define PECI_RD_SAMPLING_POINT_MAX 15 +-#define PECI_CLK_DIV_DEFAULT 0 +-#define PECI_CLK_DIV_MAX 7 +-#define PECI_MSG_TIMING_DEFAULT 1 +-#define PECI_MSG_TIMING_MAX 255 +-#define PECI_ADDR_TIMING_DEFAULT 1 +-#define PECI_ADDR_TIMING_MAX 255 +-#define PECI_CMD_TIMEOUT_MS_DEFAULT 1000 +-#define PECI_CMD_TIMEOUT_MS_MAX 60000 +- +-struct aspeed_peci { +- struct peci_adapter *adapter; +- struct device *dev; +- struct regmap *regmap; +- struct clk *clk; +- struct reset_control *rst; +- int irq; +- spinlock_t lock; /* to sync completion status handling */ +- struct completion xfer_complete; +- u32 status; +- u32 cmd_timeout_ms; +-}; +- +-static int aspeed_peci_xfer_native(struct aspeed_peci *priv, +- struct peci_xfer_msg *msg) +-{ +- long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); +- u32 peci_head, peci_state, rx_data, cmd_sts; +- unsigned long flags; +- int i, rc; +- uint reg; +- +- /* Check command sts and bus idle state */ +- rc = regmap_read_poll_timeout(priv->regmap, ASPEED_PECI_CMD, cmd_sts, +- !(cmd_sts & PECI_CMD_IDLE_MASK), +- PECI_IDLE_CHECK_INTERVAL_USEC, +- PECI_IDLE_CHECK_TIMEOUT_USEC); +- if (rc) +- return rc; /* -ETIMEDOUT */ +- +- spin_lock_irqsave(&priv->lock, flags); +- reinit_completion(&priv->xfer_complete); +- +- peci_head = FIELD_PREP(PECI_TAGET_ADDR_MASK, msg->addr) | +- FIELD_PREP(PECI_WRITE_LEN_MASK, msg->tx_len) | +- FIELD_PREP(PECI_READ_LEN_MASK, msg->rx_len); +- +- regmap_write(priv->regmap, ASPEED_PECI_CMD_CTRL, peci_head); +- +- for (i = 0; i < msg->tx_len; i += 4) { +- reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 : +- ASPEED_PECI_W_DATA4 + i % 16; +- regmap_write(priv->regmap, reg, +- le32_to_cpup((__le32 *)&msg->tx_buf[i])); +- } +- +- dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); +- print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1, +- msg->tx_buf, msg->tx_len, true); +- +- priv->status = 0; +- regmap_write(priv->regmap, ASPEED_PECI_CMD, PECI_CMD_FIRE); +- spin_unlock_irqrestore(&priv->lock, flags); +- +- err = wait_for_completion_interruptible_timeout(&priv->xfer_complete, +- timeout); +- +- spin_lock_irqsave(&priv->lock, flags); +- dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status); +- regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state); +- dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", +- FIELD_GET(PECI_CMD_STS_MASK, peci_state)); +- +- regmap_write(priv->regmap, ASPEED_PECI_CMD, 0); +- +- if (err <= 0 || priv->status != PECI_INT_CMD_DONE) { +- if (err < 0) { /* -ERESTARTSYS */ +- rc = (int)err; +- goto err_irqrestore; +- } else if (err == 0) { +- dev_dbg(priv->dev, "Timeout waiting for a response!\n"); +- rc = -ETIMEDOUT; +- goto err_irqrestore; +- } +- +- dev_dbg(priv->dev, "No valid response!\n"); +- rc = -EIO; +- goto err_irqrestore; +- } +- +- /** +- * Note that rx_len and rx_buf size can be an odd number. +- * Byte handling is more efficient. +- */ +- for (i = 0; i < msg->rx_len; i++) { +- u8 byte_offset = i % 4; +- +- if (byte_offset == 0) { +- reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 : +- ASPEED_PECI_R_DATA4 + i % 16; +- regmap_read(priv->regmap, reg, &rx_data); +- } +- +- msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3)); +- } +- +- print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1, +- msg->rx_buf, msg->rx_len, true); +- +- regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state); +- dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", +- FIELD_GET(PECI_CMD_STS_MASK, peci_state)); +- dev_dbg(priv->dev, "------------------------\n"); +- +-err_irqrestore: +- spin_unlock_irqrestore(&priv->lock, flags); +- return rc; +-} +- +-static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) +-{ +- struct aspeed_peci *priv = arg; +- u32 status_ack = 0; +- u32 status; +- +- spin_lock(&priv->lock); +- regmap_read(priv->regmap, ASPEED_PECI_INT_STS, &status); +- priv->status |= (status & PECI_INT_MASK); +- +- /** +- * In most cases, interrupt bits will be set one by one but also note +- * that multiple interrupt bits could be set at the same time. +- */ +- if (status & PECI_INT_TIMEOUT) { +- dev_dbg(priv->dev, "PECI_INT_TIMEOUT\n"); +- status_ack |= PECI_INT_TIMEOUT; +- } +- +- if (status & PECI_INT_CONNECT) { +- dev_dbg(priv->dev, "PECI_INT_CONNECT\n"); +- status_ack |= PECI_INT_CONNECT; +- } +- +- if (status & PECI_INT_W_FCS_BAD) { +- dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n"); +- status_ack |= PECI_INT_W_FCS_BAD; +- } +- +- if (status & PECI_INT_W_FCS_ABORT) { +- dev_dbg(priv->dev, "PECI_INT_W_FCS_ABORT\n"); +- status_ack |= PECI_INT_W_FCS_ABORT; +- } +- +- /** +- * All commands should be ended up with a PECI_INT_CMD_DONE bit set +- * even in an error case. +- */ +- if (status & PECI_INT_CMD_DONE) { +- dev_dbg(priv->dev, "PECI_INT_CMD_DONE\n"); +- status_ack |= PECI_INT_CMD_DONE; +- complete(&priv->xfer_complete); +- } +- +- regmap_write(priv->regmap, ASPEED_PECI_INT_STS, status_ack); +- spin_unlock(&priv->lock); +- return IRQ_HANDLED; +-} +- +-static int aspeed_peci_init_ctrl(struct aspeed_peci *priv) +-{ +- u32 msg_timing, addr_timing, rd_sampling_point; +- u32 clk_freq, clk_divisor, clk_div_val = 0; +- int ret; +- +- priv->clk = devm_clk_get(priv->dev, NULL); +- if (IS_ERR(priv->clk)) { +- dev_err(priv->dev, "Failed to get clk source.\n"); +- return PTR_ERR(priv->clk); +- } +- +- ret = clk_prepare_enable(priv->clk); +- if (ret) { +- dev_err(priv->dev, "Failed to enable clock.\n"); +- return ret; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "clock-frequency", +- &clk_freq); +- if (ret) { +- dev_err(priv->dev, +- "Could not read clock-frequency property.\n"); +- clk_disable_unprepare(priv->clk); +- return ret; +- } +- +- clk_divisor = clk_get_rate(priv->clk) / clk_freq; +- +- while ((clk_divisor >> 1) && (clk_div_val < PECI_CLK_DIV_MAX)) +- clk_div_val++; +- +- ret = of_property_read_u32(priv->dev->of_node, "msg-timing", +- &msg_timing); +- if (ret || msg_timing > PECI_MSG_TIMING_MAX) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid msg-timing : %u, Use default : %u\n", +- msg_timing, PECI_MSG_TIMING_DEFAULT); +- msg_timing = PECI_MSG_TIMING_DEFAULT; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "addr-timing", +- &addr_timing); +- if (ret || addr_timing > PECI_ADDR_TIMING_MAX) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid addr-timing : %u, Use default : %u\n", +- addr_timing, PECI_ADDR_TIMING_DEFAULT); +- addr_timing = PECI_ADDR_TIMING_DEFAULT; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "rd-sampling-point", +- &rd_sampling_point); +- if (ret || rd_sampling_point > PECI_RD_SAMPLING_POINT_MAX) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid rd-sampling-point : %u. Use default : %u\n", +- rd_sampling_point, +- PECI_RD_SAMPLING_POINT_DEFAULT); +- rd_sampling_point = PECI_RD_SAMPLING_POINT_DEFAULT; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms", +- &priv->cmd_timeout_ms); +- if (ret || priv->cmd_timeout_ms > PECI_CMD_TIMEOUT_MS_MAX || +- priv->cmd_timeout_ms == 0) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid cmd-timeout-ms : %u. Use default : %u\n", +- priv->cmd_timeout_ms, +- PECI_CMD_TIMEOUT_MS_DEFAULT); +- priv->cmd_timeout_ms = PECI_CMD_TIMEOUT_MS_DEFAULT; +- } +- +- regmap_write(priv->regmap, ASPEED_PECI_CTRL, +- FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, PECI_CLK_DIV_DEFAULT) | +- PECI_CTRL_PECI_CLK_EN); +- +- /** +- * Timing negotiation period setting. +- * The unit of the programmed value is 4 times of PECI clock period. +- */ +- regmap_write(priv->regmap, ASPEED_PECI_TIMING, +- FIELD_PREP(PECI_TIMING_MESSAGE_MASK, msg_timing) | +- FIELD_PREP(PECI_TIMING_ADDRESS_MASK, addr_timing)); +- +- /* Clear interrupts */ +- regmap_write(priv->regmap, ASPEED_PECI_INT_STS, PECI_INT_MASK); +- +- /* Enable interrupts */ +- regmap_write(priv->regmap, ASPEED_PECI_INT_CTRL, PECI_INT_MASK); +- +- /* Read sampling point and clock speed setting */ +- regmap_write(priv->regmap, ASPEED_PECI_CTRL, +- FIELD_PREP(PECI_CTRL_SAMPLING_MASK, rd_sampling_point) | +- FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, clk_div_val) | +- PECI_CTRL_PECI_EN | PECI_CTRL_PECI_CLK_EN); +- +- return 0; +-} +- +-static const struct regmap_config aspeed_peci_regmap_config = { +- .reg_bits = 32, +- .val_bits = 32, +- .reg_stride = 4, +- .max_register = ASPEED_PECI_R_DATA7, +- .val_format_endian = REGMAP_ENDIAN_LITTLE, +- .fast_io = true, +-}; +- +-static int aspeed_peci_xfer(struct peci_adapter *adapter, +- struct peci_xfer_msg *msg) +-{ +- struct aspeed_peci *priv = peci_get_adapdata(adapter); +- +- return aspeed_peci_xfer_native(priv, msg); +-} +- +-static int aspeed_peci_probe(struct platform_device *pdev) +-{ +- struct peci_adapter *adapter; +- struct aspeed_peci *priv; +- struct resource *res; +- void __iomem *base; +- u32 cmd_sts; +- int ret; +- +- adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv)); +- if (!adapter) +- return -ENOMEM; +- +- priv = peci_get_adapdata(adapter); +- priv->adapter = adapter; +- priv->dev = &pdev->dev; +- dev_set_drvdata(&pdev->dev, priv); +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(base)) { +- ret = PTR_ERR(base); +- goto err_put_adapter_dev; +- } +- +- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, +- &aspeed_peci_regmap_config); +- if (IS_ERR(priv->regmap)) { +- ret = PTR_ERR(priv->regmap); +- goto err_put_adapter_dev; +- } +- +- /** +- * We check that the regmap works on this very first access, +- * but as this is an MMIO-backed regmap, subsequent regmap +- * access is not going to fail and we skip error checks from +- * this point. +- */ +- ret = regmap_read(priv->regmap, ASPEED_PECI_CMD, &cmd_sts); +- if (ret) { +- ret = -EIO; +- goto err_put_adapter_dev; +- } +- +- priv->irq = platform_get_irq(pdev, 0); +- if (!priv->irq) { +- ret = -ENODEV; +- goto err_put_adapter_dev; +- } +- +- ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, +- 0, "peci-aspeed-irq", priv); +- if (ret) +- goto err_put_adapter_dev; +- +- init_completion(&priv->xfer_complete); +- spin_lock_init(&priv->lock); +- +- priv->adapter->owner = THIS_MODULE; +- priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev)); +- strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name)); +- priv->adapter->xfer = aspeed_peci_xfer; +- +- priv->rst = devm_reset_control_get(&pdev->dev, NULL); +- if (IS_ERR(priv->rst)) { +- dev_err(&pdev->dev, +- "missing or invalid reset controller entry"); +- ret = PTR_ERR(priv->rst); +- goto err_put_adapter_dev; +- } +- reset_control_deassert(priv->rst); +- +- ret = aspeed_peci_init_ctrl(priv); +- if (ret) +- goto err_put_adapter_dev; +- +- ret = peci_add_adapter(priv->adapter); +- if (ret) +- goto err_put_adapter_dev; +- +- dev_info(&pdev->dev, "peci bus %d registered, irq %d\n", +- priv->adapter->nr, priv->irq); +- +- return 0; +- +-err_put_adapter_dev: +- put_device(&adapter->dev); +- return ret; +-} +- +-static int aspeed_peci_remove(struct platform_device *pdev) +-{ +- struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev); +- +- clk_disable_unprepare(priv->clk); +- reset_control_assert(priv->rst); +- peci_del_adapter(priv->adapter); +- of_node_put(priv->adapter->dev.of_node); +- +- return 0; +-} +- +-static const struct of_device_id aspeed_peci_of_table[] = { +- { .compatible = "aspeed,ast2400-peci", }, +- { .compatible = "aspeed,ast2500-peci", }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); +- +-static struct platform_driver aspeed_peci_driver = { +- .probe = aspeed_peci_probe, +- .remove = aspeed_peci_remove, +- .driver = { +- .name = "peci-aspeed", +- .of_match_table = of_match_ptr(aspeed_peci_of_table), +- }, +-}; +-module_platform_driver(aspeed_peci_driver); +- +-MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); +-MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +-MODULE_DESCRIPTION("ASPEED PECI driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index fac8c72dcda8..e2ef013e5002 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1,38 +1,31 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/crc8.h> + #include <linux/delay.h> +-#include <linux/fs.h> ++#include <linux/mm.h> + #include <linux/module.h> + #include <linux/of_device.h> + #include <linux/peci.h> + #include <linux/pm_domain.h> + #include <linux/pm_runtime.h> ++#include <linux/sched/task_stack.h> + #include <linux/slab.h> +-#include <linux/uaccess.h> + + /* Mask for getting minor revision number from DIB */ + #define REVISION_NUM_MASK GENMASK(15, 8) + +-/* CRC8 table for Assure Write Frame Check */ ++/* CRC8 table for Assured Write Frame Check */ + #define PECI_CRC8_POLYNOMIAL 0x07 + DECLARE_CRC8_TABLE(peci_crc8_table); + +-static struct device_type peci_adapter_type; +-static struct device_type peci_client_type; +- +-/* Max number of peci cdev */ +-#define PECI_CDEV_MAX 16 +- +-static dev_t peci_devt; + static bool is_registered; + + static DEFINE_MUTEX(core_lock); + static DEFINE_IDR(peci_adapter_idr); + +-static struct peci_adapter *peci_get_adapter(int nr) ++struct peci_adapter *peci_get_adapter(int nr) + { + struct peci_adapter *adapter; + +@@ -48,10 +41,12 @@ static struct peci_adapter *peci_get_adapter(int nr) + + out_unlock: + mutex_unlock(&core_lock); ++ + return adapter; + } ++EXPORT_SYMBOL_GPL(peci_get_adapter); + +-static void peci_put_adapter(struct peci_adapter *adapter) ++void peci_put_adapter(struct peci_adapter *adapter) + { + if (!adapter) + return; +@@ -59,6 +54,7 @@ static void peci_put_adapter(struct peci_adapter *adapter) + put_device(&adapter->dev); + module_put(adapter->owner); + } ++EXPORT_SYMBOL_GPL(peci_put_adapter); + + static ssize_t name_show(struct device *dev, + struct device_attribute *attr, +@@ -84,10 +80,11 @@ static struct attribute *peci_device_attrs[] = { + }; + ATTRIBUTE_GROUPS(peci_device); + +-static struct device_type peci_client_type = { ++struct device_type peci_client_type = { + .groups = peci_device_groups, + .release = peci_client_dev_release, + }; ++EXPORT_SYMBOL_GPL(peci_client_type); + + /** + * peci_verify_client - return parameter as peci_client, or NULL +@@ -103,19 +100,120 @@ struct peci_client *peci_verify_client(struct device *dev) + } + EXPORT_SYMBOL_GPL(peci_verify_client); + +-static u8 peci_aw_fcs(u8 *data, int len) ++/** ++ * peci_get_xfer_msg() - get a DMA safe peci_xfer_msg for the given tx and rx ++ * length ++ * @tx_len: the length of tx_buf. May be 0 if tx_buf isn't needed. ++ * @rx_len: the length of rx_buf. May be 0 if rx_buf isn't needed. ++ * ++ * Return: NULL if a DMA safe buffer was not obtained. ++ * Or a valid pointer to be used with DMA. After use, release it by ++ * calling peci_put_xfer_msg(). ++ * ++ * This function must only be called from process context! ++ */ ++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len) ++{ ++ struct peci_xfer_msg *msg; ++ u8 *tx_buf, *rx_buf; ++ ++ if (tx_len) { ++ tx_buf = kzalloc(tx_len, GFP_KERNEL); ++ if (!tx_buf) ++ return NULL; ++ } else { ++ tx_buf = NULL; ++ } ++ ++ if (rx_len) { ++ rx_buf = kzalloc(rx_len, GFP_KERNEL); ++ if (!rx_buf) ++ goto err_free_tx_buf; ++ } else { ++ rx_buf = NULL; ++ } ++ ++ msg = kzalloc(sizeof(struct peci_xfer_msg), GFP_KERNEL); ++ if (!msg) ++ goto err_free_tx_rx_buf; ++ ++ msg->tx_len = tx_len; ++ msg->tx_buf = tx_buf; ++ msg->rx_len = rx_len; ++ msg->rx_buf = rx_buf; ++ ++ return msg; ++ ++err_free_tx_rx_buf: ++ kfree(rx_buf); ++err_free_tx_buf: ++ kfree(tx_buf); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(peci_get_xfer_msg); ++ ++/** ++ * peci_put_xfer_msg - release a DMA safe peci_xfer_msg ++ * @msg: the message obtained from peci_get_xfer_msg(). May be NULL. ++ */ ++void peci_put_xfer_msg(struct peci_xfer_msg *msg) ++{ ++ if (!msg) ++ return; ++ ++ kfree(msg->rx_buf); ++ kfree(msg->tx_buf); ++ kfree(msg); ++} ++EXPORT_SYMBOL_GPL(peci_put_xfer_msg); ++ ++/* Calculate an Assured Write Frame Check Sequence byte */ ++static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs) + { +- return crc8(peci_crc8_table, data, (size_t)len, 0); ++ u8 *tmp_buf; ++ ++ /* Allocate a temporary buffer to use a contiguous byte array */ ++ tmp_buf = kmalloc(len, GFP_KERNEL); ++ if (!tmp_buf) ++ return -ENOMEM; ++ ++ tmp_buf[0] = msg->addr; ++ tmp_buf[1] = msg->tx_len; ++ tmp_buf[2] = msg->rx_len; ++ memcpy(&tmp_buf[3], msg->tx_buf, len - 3); ++ ++ *aw_fcs = crc8(peci_crc8_table, tmp_buf, (size_t)len, 0); ++ ++ kfree(tmp_buf); ++ ++ return 0; + } + + static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg, + bool do_retry, bool has_aw_fcs) + { +- ktime_t start, end; +- s64 elapsed_ms; +- int rc = 0; ++ ulong timeout = jiffies; ++ u8 aw_fcs; ++ int ret; + +- /** ++ /* ++ * In case if adapter uses DMA, check at here whether tx and rx buffers ++ * are DMA capable or not. ++ */ ++ if (IS_ENABLED(CONFIG_HAS_DMA) && adapter->use_dma) { ++ if (is_vmalloc_addr(msg->tx_buf) || ++ is_vmalloc_addr(msg->rx_buf)) { ++ WARN_ONCE(1, "xfer msg is not dma capable\n"); ++ return -EAGAIN; ++ } else if (object_is_on_stack(msg->tx_buf) || ++ object_is_on_stack(msg->rx_buf)) { ++ WARN_ONCE(1, "xfer msg is on stack\n"); ++ return -EAGAIN; ++ } ++ } ++ ++ /* + * For some commands, the PECI originator may need to retry a command if + * the processor PECI client responds with a 0x8x completion code. In + * each instance, the processor PECI client may have started the +@@ -125,55 +223,56 @@ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg, + */ + + if (do_retry) +- start = ktime_get(); ++ timeout += msecs_to_jiffies(PECI_DEV_RETRY_TIME_MS); + +- do { +- rc = adapter->xfer(adapter, msg); ++ for (;;) { ++ ret = adapter->xfer(adapter, msg); + +- if (!do_retry || rc) ++ if (!do_retry || ret) + break; + +- if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS) ++ if (msg->rx_buf[0] == PECI_DEV_CC_SUCCESS) + break; + + /* Retry is needed when completion code is 0x8x */ +- if ((msg->rx_buf[0] & DEV_PECI_CC_RETRY_CHECK_MASK) != +- DEV_PECI_CC_NEED_RETRY) { +- rc = -EIO; ++ if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) != ++ PECI_DEV_CC_NEED_RETRY) { ++ ret = -EIO; + break; + } + + /* Set the retry bit to indicate a retry attempt */ +- msg->tx_buf[1] |= DEV_PECI_RETRY_BIT; ++ msg->tx_buf[1] |= PECI_DEV_RETRY_BIT; + + /* Recalculate the AW FCS if it has one */ +- if (has_aw_fcs) +- msg->tx_buf[msg->tx_len - 1] = 0x80 ^ +- peci_aw_fcs((u8 *)msg, +- 2 + msg->tx_len); ++ if (has_aw_fcs) { ++ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs); ++ if (ret) ++ break; + +- /** ++ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs; ++ } ++ ++ /* + * Retry for at least 250ms before returning an error. + * Retry interval guideline: + * No minimum < Retry Interval < No maximum + * (recommend 10ms) + */ +- end = ktime_get(); +- elapsed_ms = ktime_to_ms(ktime_sub(end, start)); +- if (elapsed_ms >= DEV_PECI_RETRY_TIME_MS) { ++ if (time_after(jiffies, timeout)) { + dev_dbg(&adapter->dev, "Timeout retrying xfer!\n"); +- rc = -ETIMEDOUT; ++ ret = -ETIMEDOUT; + break; + } + +- usleep_range((DEV_PECI_RETRY_INTERVAL_USEC >> 2) + 1, +- DEV_PECI_RETRY_INTERVAL_USEC); +- } while (true); ++ usleep_range((PECI_DEV_RETRY_INTERVAL_USEC >> 2) + 1, ++ PECI_DEV_RETRY_INTERVAL_USEC); ++ } + +- if (rc) +- dev_dbg(&adapter->dev, "xfer error, rc: %d\n", rc); ++ if (ret) ++ dev_dbg(&adapter->dev, "xfer error: %d\n", ret); + +- return rc; ++ return ret; + } + + static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg) +@@ -190,34 +289,37 @@ static int peci_xfer_with_retries(struct peci_adapter *adapter, + + static int peci_scan_cmd_mask(struct peci_adapter *adapter) + { +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; + u8 revision; +- int rc = 0; ++ int ret; + u64 dib; + + /* Update command mask just once */ + if (adapter->cmd_mask & BIT(PECI_CMD_XFER)) + return 0; + +- msg.addr = PECI_BASE_ADDR; +- msg.tx_len = GET_DIB_WR_LEN; +- msg.rx_len = GET_DIB_RD_LEN; +- msg.tx_buf[0] = GET_DIB_PECI_CMD; ++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = PECI_BASE_ADDR; ++ msg->tx_buf[0] = PECI_GET_DIB_CMD; + +- rc = peci_xfer(adapter, &msg); +- if (rc) +- return rc; ++ ret = peci_xfer(adapter, msg); ++ if (ret) ++ return ret; + +- dib = le64_to_cpup((__le64 *)msg.rx_buf); ++ dib = le64_to_cpup((__le64 *)msg->rx_buf); + + /* Check special case for Get DIB command */ + if (dib == 0) { + dev_dbg(&adapter->dev, "DIB read as 0\n"); +- return -EIO; ++ ret = -EIO; ++ goto out; + } + + /** +- * Setting up the supporting commands based on minor revision number. ++ * Setting up the supporting commands based on revision number. + * See PECI Spec Table 3-1. + */ + revision = FIELD_GET(REVISION_NUM_MASK, dib); +@@ -243,10 +345,14 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter) + adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB); + adapter->cmd_mask |= BIT(PECI_CMD_PING); + +- return rc; ++out: ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd) ++static int peci_check_cmd_support(struct peci_adapter *adapter, ++ enum peci_cmd cmd) + { + if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) && + peci_scan_cmd_mask(adapter) < 0) { +@@ -262,70 +368,87 @@ static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd) + return 0; + } + +-static int peci_ioctl_xfer(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg) + { + struct peci_xfer_msg *msg = vmsg; + + return peci_xfer(adapter, msg); + } + +-static int peci_ioctl_ping(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_ping(struct peci_adapter *adapter, void *vmsg) + { + struct peci_ping_msg *umsg = vmsg; +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; ++ int ret; + +- msg.addr = umsg->addr; +- msg.tx_len = 0; +- msg.rx_len = 0; ++ msg = peci_get_xfer_msg(0, 0); ++ if (!msg) ++ return -ENOMEM; + +- return peci_xfer(adapter, &msg); ++ msg->addr = umsg->addr; ++ ++ ret = peci_xfer(adapter, msg); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_ioctl_get_dib(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_get_dib(struct peci_adapter *adapter, void *vmsg) + { + struct peci_get_dib_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc; ++ struct peci_xfer_msg *msg; ++ int ret; + +- msg.addr = umsg->addr; +- msg.tx_len = GET_DIB_WR_LEN; +- msg.rx_len = GET_DIB_RD_LEN; +- msg.tx_buf[0] = GET_DIB_PECI_CMD; ++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); ++ if (!msg) ++ return -ENOMEM; + +- rc = peci_xfer(adapter, &msg); +- if (rc) +- return rc; ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_GET_DIB_CMD; + +- umsg->dib = le64_to_cpup((__le64 *)msg.rx_buf); ++ ret = peci_xfer(adapter, msg); ++ if (ret) ++ goto out; + +- return 0; ++ umsg->dib = le64_to_cpup((__le64 *)msg->rx_buf); ++ ++out: ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_ioctl_get_temp(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_get_temp(struct peci_adapter *adapter, void *vmsg) + { + struct peci_get_temp_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc; ++ struct peci_xfer_msg *msg; ++ int ret; + +- msg.addr = umsg->addr; +- msg.tx_len = GET_TEMP_WR_LEN; +- msg.rx_len = GET_TEMP_RD_LEN; +- msg.tx_buf[0] = GET_TEMP_PECI_CMD; ++ msg = peci_get_xfer_msg(PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN); ++ if (!msg) ++ return -ENOMEM; + +- rc = peci_xfer(adapter, &msg); +- if (rc) +- return rc; ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_GET_TEMP_CMD; + +- umsg->temp_raw = le16_to_cpup((__le16 *)msg.rx_buf); ++ ret = peci_xfer(adapter, msg); ++ if (ret) ++ goto out; + +- return 0; ++ umsg->temp_raw = le16_to_cpup((__le16 *)msg->rx_buf); ++ ++out: ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_pkg_cfg_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0; ++ struct peci_xfer_msg *msg; ++ int ret; + + /* Per the PECI spec, the read length must be a byte, word, or dword */ + if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) { +@@ -334,29 +457,34 @@ static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + +- msg.addr = umsg->addr; +- msg.tx_len = RDPKGCFG_WRITE_LEN; +- /* read lengths of 1 and 2 result in an error, so only use 4 for now */ +- msg.rx_len = RDPKGCFG_READ_LEN_BASE + umsg->rx_len; +- msg.tx_buf[0] = RDPKGCFG_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ +- /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */ +- msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ +- msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ ++ msg = peci_get_xfer_msg(PECI_RDPKGCFG_WRITE_LEN, ++ PECI_RDPKGCFG_READ_LEN_BASE + umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDPKGCFG_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */ ++ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ ++ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len); + +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len); ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + { + struct peci_wr_pkg_cfg_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0, i; ++ struct peci_xfer_msg *msg; ++ int ret, i; ++ u8 aw_fcs; + + /* Per the PECI spec, the write length must be a dword */ + if (umsg->tx_len != 4) { +@@ -365,86 +493,113 @@ static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + +- msg.addr = umsg->addr; +- msg.tx_len = WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len; +- /* read lengths of 1 and 2 result in an error, so only use 4 for now */ +- msg.rx_len = WRPKGCFG_READ_LEN; +- msg.tx_buf[0] = WRPKGCFG_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ msg = peci_get_xfer_msg(PECI_WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len, ++ PECI_WRPKGCFG_READ_LEN); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_WRPKGCFG_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ + /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */ +- msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ +- msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ ++ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */ ++ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ ++ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ + for (i = 0; i < umsg->tx_len; i++) +- msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ ++ /* Add an Assured Write Frame Check Sequence byte */ ++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs); ++ if (ret) ++ goto out; ++ ++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs; + +- /* Add an Assure Write Frame Check Sequence byte */ +- msg.tx_buf[5 + i] = 0x80 ^ +- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len); ++ ret = peci_xfer_with_retries(adapter, msg, true); + +- rc = peci_xfer_with_retries(adapter, &msg, true); ++out: ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_rd_ia_msr(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_rd_ia_msr(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_ia_msr_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0; +- +- msg.addr = umsg->addr; +- msg.tx_len = RDIAMSR_WRITE_LEN; +- msg.rx_len = RDIAMSR_READ_LEN; +- msg.tx_buf[0] = RDIAMSR_PECI_CMD; +- msg.tx_buf[1] = 0; +- msg.tx_buf[2] = umsg->thread_id; +- msg.tx_buf[3] = (u8)umsg->address; +- msg.tx_buf[4] = (u8)(umsg->address >> 8); +- +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(&umsg->value, &msg.rx_buf[1], sizeof(uint64_t)); +- +- return rc; ++ struct peci_xfer_msg *msg; ++ int ret; ++ ++ msg = peci_get_xfer_msg(PECI_RDIAMSR_WRITE_LEN, PECI_RDIAMSR_READ_LEN); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDIAMSR_CMD; ++ msg->tx_buf[1] = 0; ++ msg->tx_buf[2] = umsg->thread_id; ++ msg->tx_buf[3] = (u8)umsg->address; ++ msg->tx_buf[4] = (u8)(umsg->address >> 8); ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t)); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; ++} ++ ++static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg) ++{ ++ return -ENOSYS; /* Not implemented yet */ + } + +-static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_pci_cfg_msg *umsg = vmsg; +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; + u32 address; +- int rc = 0; ++ int ret; ++ ++ msg = peci_get_xfer_msg(PECI_RDPCICFG_WRITE_LEN, ++ PECI_RDPCICFG_READ_LEN); ++ if (!msg) ++ return -ENOMEM; + + address = umsg->reg; /* [11:0] - Register */ + address |= (u32)umsg->function << 12; /* [14:12] - Function */ + address |= (u32)umsg->device << 15; /* [19:15] - Device */ + address |= (u32)umsg->bus << 20; /* [27:20] - Bus */ + /* [31:28] - Reserved */ +- msg.addr = umsg->addr; +- msg.tx_len = RDPCICFG_WRITE_LEN; +- msg.rx_len = RDPCICFG_READ_LEN; +- msg.tx_buf[0] = RDPCICFG_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDPCICFG_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ + /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = (u8)address; /* LSB - PCI Config Address */ +- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */ +- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */ +- msg.tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */ ++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Config Address */ ++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */ ++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */ ++ msg->tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */ ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->pci_config, &msg->rx_buf[1], 4); + +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(umsg->pci_config, &msg.rx_buf[1], 4); ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg) ++{ ++ return -ENOSYS; /* Not implemented yet */ ++} ++ ++static int peci_cmd_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_pci_cfg_local_msg *umsg = vmsg; +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; + u32 address; +- int rc = 0; ++ int ret; + + /* Per the PECI spec, the read length must be a byte, word, or dword */ + if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) { +@@ -453,34 +608,41 @@ static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + ++ msg = peci_get_xfer_msg(PECI_RDPCICFGLOCAL_WRITE_LEN, ++ PECI_RDPCICFGLOCAL_READ_LEN_BASE + ++ umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ + address = umsg->reg; /* [11:0] - Register */ + address |= (u32)umsg->function << 12; /* [14:12] - Function */ + address |= (u32)umsg->device << 15; /* [19:15] - Device */ + address |= (u32)umsg->bus << 20; /* [23:20] - Bus */ + +- msg.addr = umsg->addr; +- msg.tx_len = RDPCICFGLOCAL_WRITE_LEN; +- msg.rx_len = RDPCICFGLOCAL_READ_LEN_BASE + umsg->rx_len; +- msg.tx_buf[0] = RDPCICFGLOCAL_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ +- /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ +- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ +- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDPCICFGLOCAL_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ ++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ ++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->pci_config, &msg->rx_buf[1], umsg->rx_len); + +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(umsg->pci_config, &msg.rx_buf[1], umsg->rx_len); ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + { + struct peci_wr_pci_cfg_local_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0, i; ++ struct peci_xfer_msg *msg; + u32 address; ++ int ret, i; ++ u8 aw_fcs; + + /* Per the PECI spec, the write length must be a byte, word, or dword */ + if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) { +@@ -489,47 +651,56 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + ++ msg = peci_get_xfer_msg(PECI_WRPCICFGLOCAL_WRITE_LEN_BASE + ++ umsg->tx_len, PECI_WRPCICFGLOCAL_READ_LEN); ++ if (!msg) ++ return -ENOMEM; ++ + address = umsg->reg; /* [11:0] - Register */ + address |= (u32)umsg->function << 12; /* [14:12] - Function */ + address |= (u32)umsg->device << 15; /* [19:15] - Device */ + address |= (u32)umsg->bus << 20; /* [23:20] - Bus */ + +- msg.addr = umsg->addr; +- msg.tx_len = WRPCICFGLOCAL_WRITE_LEN_BASE + umsg->tx_len; +- msg.rx_len = WRPCICFGLOCAL_READ_LEN; +- msg.tx_buf[0] = WRPCICFGLOCAL_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ +- /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ +- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ +- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_WRPCICFGLOCAL_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ ++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ ++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ + for (i = 0; i < umsg->tx_len; i++) +- msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ ++ /* Add an Assured Write Frame Check Sequence byte */ ++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs); ++ if (ret) ++ goto out; ++ ++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs; + +- /* Add an Assure Write Frame Check Sequence byte */ +- msg.tx_buf[5 + i] = 0x80 ^ +- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len); ++ ret = peci_xfer_with_retries(adapter, msg, true); + +- rc = peci_xfer_with_retries(adapter, &msg, true); ++out: ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-typedef int (*peci_ioctl_fn_type)(struct peci_adapter *, void *); +- +-static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { +- peci_ioctl_xfer, +- peci_ioctl_ping, +- peci_ioctl_get_dib, +- peci_ioctl_get_temp, +- peci_ioctl_rd_pkg_cfg, +- peci_ioctl_wr_pkg_cfg, +- peci_ioctl_rd_ia_msr, +- NULL, /* Reserved */ +- peci_ioctl_rd_pci_cfg, +- NULL, /* Reserved */ +- peci_ioctl_rd_pci_cfg_local, +- peci_ioctl_wr_pci_cfg_local, ++typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *); ++ ++static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = { ++ peci_cmd_xfer, ++ peci_cmd_ping, ++ peci_cmd_get_dib, ++ peci_cmd_get_temp, ++ peci_cmd_rd_pkg_cfg, ++ peci_cmd_wr_pkg_cfg, ++ peci_cmd_rd_ia_msr, ++ peci_cmd_wr_ia_msr, ++ peci_cmd_rd_pci_cfg, ++ peci_cmd_wr_pci_cfg, ++ peci_cmd_rd_pci_cfg_local, ++ peci_cmd_wr_pci_cfg_local, + }; + + /** +@@ -545,109 +716,28 @@ static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { + */ + int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg) + { +- int rc = 0; ++ int ret; + + if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER) +- return -EINVAL; ++ return -ENOTTY; + + dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd); + +- if (!peci_ioctl_fn[cmd]) ++ if (!peci_cmd_fn[cmd]) + return -EINVAL; + +- rt_mutex_lock(&adapter->bus_lock); ++ mutex_lock(&adapter->bus_lock); + +- rc = peci_cmd_support(adapter, cmd); +- if (!rc) +- rc = peci_ioctl_fn[cmd](adapter, vmsg); ++ ret = peci_check_cmd_support(adapter, cmd); ++ if (!ret) ++ ret = peci_cmd_fn[cmd](adapter, vmsg); + +- rt_mutex_unlock(&adapter->bus_lock); ++ mutex_unlock(&adapter->bus_lock); + +- return rc; ++ return ret; + } + EXPORT_SYMBOL_GPL(peci_command); + +-static long peci_ioctl(struct file *file, unsigned int iocmd, unsigned long arg) +-{ +- struct peci_adapter *adapter = file->private_data; +- void __user *argp = (void __user *)arg; +- unsigned int msg_len; +- enum peci_cmd cmd; +- int rc = 0; +- u8 *msg; +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- +- dev_dbg(&adapter->dev, "ioctl, cmd=0x%x, arg=0x%lx\n", iocmd, arg); +- +- switch (iocmd) { +- case PECI_IOC_XFER: +- case PECI_IOC_PING: +- case PECI_IOC_GET_DIB: +- case PECI_IOC_GET_TEMP: +- case PECI_IOC_RD_PKG_CFG: +- case PECI_IOC_WR_PKG_CFG: +- case PECI_IOC_RD_IA_MSR: +- case PECI_IOC_RD_PCI_CFG: +- case PECI_IOC_RD_PCI_CFG_LOCAL: +- case PECI_IOC_WR_PCI_CFG_LOCAL: +- cmd = _IOC_NR(iocmd); +- msg_len = _IOC_SIZE(iocmd); +- break; +- +- default: +- dev_dbg(&adapter->dev, "Invalid ioctl cmd : 0x%x\n", iocmd); +- return -ENOTTY; +- } +- +- if (!access_ok(VERIFY_WRITE, argp, msg_len)) +- return -EFAULT; +- +- msg = memdup_user(argp, msg_len); +- if (IS_ERR(msg)) +- return PTR_ERR(msg); +- +- rc = peci_command(adapter, cmd, msg); +- +- if (!rc && copy_to_user(argp, msg, msg_len)) +- rc = -EFAULT; +- +- kfree(msg); +- return (long)rc; +-} +- +-static int peci_open(struct inode *inode, struct file *file) +-{ +- unsigned int minor = iminor(inode); +- struct peci_adapter *adapter; +- +- adapter = peci_get_adapter(minor); +- if (!adapter) +- return -ENODEV; +- +- file->private_data = adapter; +- +- return 0; +-} +- +-static int peci_release(struct inode *inode, struct file *file) +-{ +- struct peci_adapter *adapter = file->private_data; +- +- peci_put_adapter(adapter); +- file->private_data = NULL; +- +- return 0; +-} +- +-static const struct file_operations peci_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = peci_ioctl, +- .open = peci_open, +- .release = peci_release, +-}; +- + static int peci_detect(struct peci_adapter *adapter, u8 addr) + { + struct peci_ping_msg msg; +@@ -666,9 +756,9 @@ peci_of_match_device(const struct of_device_id *matches, + return NULL; + + return of_match_device(matches, &client->dev); +-#else ++#else /* CONFIG_OF */ + return NULL; +-#endif ++#endif /* CONFIG_OF */ + } + + static const struct peci_device_id * +@@ -737,6 +827,7 @@ static int peci_device_probe(struct device *dev) + + err_detach_pm_domain: + dev_pm_domain_detach(&client->dev, true); ++ + return status; + } + +@@ -775,13 +866,14 @@ static void peci_device_shutdown(struct device *dev) + driver->shutdown(client); + } + +-static struct bus_type peci_bus_type = { ++struct bus_type peci_bus_type = { + .name = "peci", + .match = peci_device_match, + .probe = peci_device_probe, + .remove = peci_device_remove, + .shutdown = peci_device_shutdown, + }; ++EXPORT_SYMBOL_GPL(peci_bus_type); + + static int peci_check_addr_validity(u8 addr) + { +@@ -814,18 +906,18 @@ static int peci_check_client_busy(struct device *dev, void *client_new_p) + int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id) + { + struct peci_rd_pkg_cfg_msg msg; +- int rc; ++ int ret; + + msg.addr = addr; +- msg.index = MBX_INDEX_CPU_ID; +- msg.param = PKG_ID_CPU_ID; ++ msg.index = PECI_MBX_INDEX_CPU_ID; ++ msg.param = PECI_PKG_ID_CPU_ID; + msg.rx_len = 4; + +- rc = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg); +- if (!rc) ++ ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg); ++ if (!ret) + *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config); + +- return rc; ++ return ret; + } + EXPORT_SYMBOL_GPL(peci_get_cpu_id); + +@@ -833,7 +925,7 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter, + struct peci_board_info const *info) + { + struct peci_client *client; +- int rc; ++ int ret; + + /* Increase reference count for the adapter assigned */ + if (!peci_get_adapter(adapter->nr)) +@@ -847,46 +939,49 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter, + client->addr = info->addr; + strlcpy(client->name, info->type, sizeof(client->name)); + +- rc = peci_check_addr_validity(client->addr); +- if (rc) { ++ ret = peci_check_addr_validity(client->addr); ++ if (ret) { + dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n", + client->addr); + goto err_free_client_silent; + } + + /* Check online status of client */ +- rc = peci_detect(adapter, client->addr); +- if (rc) ++ ret = peci_detect(adapter, client->addr); ++ if (ret) + goto err_free_client; + +- rc = device_for_each_child(&adapter->dev, client, +- peci_check_client_busy); +- if (rc) ++ ret = device_for_each_child(&adapter->dev, client, ++ peci_check_client_busy); ++ if (ret) + goto err_free_client; + + client->dev.parent = &client->adapter->dev; + client->dev.bus = &peci_bus_type; + client->dev.type = &peci_client_type; +- client->dev.of_node = info->of_node; ++ client->dev.of_node = of_node_get(info->of_node); + dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr); + +- rc = device_register(&client->dev); +- if (rc) +- goto err_free_client; ++ ret = device_register(&client->dev); ++ if (ret) ++ goto err_put_of_node; + + dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", + client->name, dev_name(&client->dev)); + + return client; + ++err_put_of_node: ++ of_node_put(info->of_node); + err_free_client: + dev_err(&adapter->dev, + "Failed to register peci client %s at 0x%02x (%d)\n", +- client->name, client->addr, rc); ++ client->name, client->addr, ret); + err_free_client_silent: + kfree(client); + err_put_adapter: + peci_put_adapter(adapter); ++ + return NULL; + } + +@@ -895,8 +990,10 @@ static void peci_unregister_device(struct peci_client *client) + if (!client) + return; + +- if (client->dev.of_node) ++ if (client->dev.of_node) { + of_node_clear_flag(client->dev.of_node, OF_POPULATED); ++ of_node_put(client->dev.of_node); ++ } + + device_unregister(&client->dev); + } +@@ -916,7 +1013,7 @@ static void peci_adapter_dev_release(struct device *dev) + + dev_dbg(dev, "%s: %s\n", __func__, adapter->name); + mutex_destroy(&adapter->userspace_clients_lock); +- rt_mutex_destroy(&adapter->bus_lock); ++ mutex_destroy(&adapter->bus_lock); + kfree(adapter); + } + +@@ -928,7 +1025,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + struct peci_board_info info = {}; + struct peci_client *client; + char *blank, end; +- int rc; ++ short addr; ++ int ret; + + /* Parse device type */ + blank = strchr(buf, ' '); +@@ -943,16 +1041,17 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ +- rc = sscanf(++blank, "%hi%c", &info.addr, &end); +- if (rc < 1) { ++ ret = sscanf(++blank, "%hi%c", &addr, &end); ++ if (ret < 1) { + dev_err(dev, "%s: Can't parse client address\n", "new_device"); + return -EINVAL; + } +- if (rc > 1 && end != '\n') { ++ if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "new_device"); + return -EINVAL; + } + ++ info.addr = (u8)addr; + client = peci_new_device(adapter, &info); + if (!client) + return -EINVAL; +@@ -961,8 +1060,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + mutex_lock(&adapter->userspace_clients_lock); + list_add_tail(&client->detected, &adapter->userspace_clients); + mutex_unlock(&adapter->userspace_clients_lock); +- dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", +- info.type, info.addr); ++ dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", ++ info.type, info.addr); + + return count; + } +@@ -975,9 +1074,9 @@ static ssize_t peci_sysfs_delete_device(struct device *dev, + struct peci_adapter *adapter = to_peci_adapter(dev); + struct peci_client *client, *next; + struct peci_board_info info = {}; +- struct peci_driver *driver; + char *blank, end; +- int rc; ++ short addr; ++ int ret; + + /* Parse device type */ + blank = strchr(buf, ' '); +@@ -992,41 +1091,41 @@ static ssize_t peci_sysfs_delete_device(struct device *dev, + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ +- rc = sscanf(++blank, "%hi%c", &info.addr, &end); +- if (rc < 1) { ++ ret = sscanf(++blank, "%hi%c", &addr, &end); ++ if (ret < 1) { + dev_err(dev, "%s: Can't parse client address\n", + "delete_device"); + return -EINVAL; + } +- if (rc > 1 && end != '\n') { ++ if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "delete_device"); + return -EINVAL; + } + ++ info.addr = (u8)addr; ++ + /* Make sure the device was added through sysfs */ +- rc = -ENOENT; ++ ret = -ENOENT; + mutex_lock(&adapter->userspace_clients_lock); + list_for_each_entry_safe(client, next, &adapter->userspace_clients, + detected) { +- driver = to_peci_driver(client->dev.driver); +- + if (client->addr == info.addr && + !strncmp(client->name, info.type, PECI_NAME_SIZE)) { +- dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", +- "delete_device", client->name, client->addr); ++ dev_dbg(dev, "%s: Deleting device %s at 0x%02hx\n", ++ "delete_device", client->name, client->addr); + list_del(&client->detected); + peci_unregister_device(client); +- rc = count; ++ ret = count; + break; + } + } + mutex_unlock(&adapter->userspace_clients_lock); + +- if (rc < 0) +- dev_err(dev, "%s: Can't find device in list\n", ++ if (ret < 0) ++ dev_dbg(dev, "%s: Can't find device in list\n", + "delete_device"); + +- return rc; ++ return ret; + } + static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL, + peci_sysfs_delete_device); +@@ -1039,10 +1138,11 @@ static struct attribute *peci_adapter_attrs[] = { + }; + ATTRIBUTE_GROUPS(peci_adapter); + +-static struct device_type peci_adapter_type = { ++struct device_type peci_adapter_type = { + .groups = peci_adapter_groups, + .release = peci_adapter_dev_release, + }; ++EXPORT_SYMBOL_GPL(peci_adapter_type); + + /** + * peci_verify_adapter - return parameter as peci_adapter, or NULL +@@ -1063,32 +1163,26 @@ static struct peci_client *peci_of_register_device(struct peci_adapter *adapter, + struct device_node *node) + { + struct peci_board_info info = {}; +- struct peci_client *result; +- const __be32 *addr_be; +- int len; ++ struct peci_client *client; ++ u32 addr; ++ int ret; + + dev_dbg(&adapter->dev, "register %pOF\n", node); + +- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { +- dev_err(&adapter->dev, "modalias failure on %pOF\n", node); +- return ERR_PTR(-EINVAL); +- } +- +- addr_be = of_get_property(node, "reg", &len); +- if (!addr_be || len < sizeof(*addr_be)) { ++ ret = of_property_read_u32(node, "reg", &addr); ++ if (ret) { + dev_err(&adapter->dev, "invalid reg on %pOF\n", node); +- return ERR_PTR(-EINVAL); ++ return ERR_PTR(ret); + } + +- info.addr = be32_to_cpup(addr_be); +- info.of_node = of_node_get(node); ++ info.addr = addr; ++ info.of_node = node; + +- result = peci_new_device(adapter, &info); +- if (!result) +- result = ERR_PTR(-EINVAL); ++ client = peci_new_device(adapter, &info); ++ if (!client) ++ client = ERR_PTR(-EINVAL); + +- of_node_put(node); +- return result; ++ return client; + } + + static void peci_of_register_devices(struct peci_adapter *adapter) +@@ -1119,7 +1213,7 @@ static void peci_of_register_devices(struct peci_adapter *adapter) + + of_node_put(bus); + } +-#else ++#else /* CONFIG_OF */ + static void peci_of_register_devices(struct peci_adapter *adapter) { } + #endif /* CONFIG_OF */ + +@@ -1163,9 +1257,7 @@ static struct peci_adapter *peci_of_find_adapter(struct device_node *node) + return adapter; + } + +-static int peci_of_notify(struct notifier_block *nb, +- unsigned long action, +- void *arg) ++static int peci_of_notify(struct notifier_block *nb, ulong action, void *arg) + { + struct of_reconfig_data *rd = arg; + struct peci_adapter *adapter; +@@ -1216,7 +1308,7 @@ static int peci_of_notify(struct notifier_block *nb, + static struct notifier_block peci_of_notifier = { + .notifier_call = peci_of_notify, + }; +-#else ++#else /* CONFIG_OF_DYNAMIC */ + extern struct notifier_block peci_of_notifier; + #endif /* CONFIG_OF_DYNAMIC */ + +@@ -1240,7 +1332,7 @@ extern struct notifier_block peci_of_notifier; + * + * Return: the peci_adapter structure on success, else NULL. + */ +-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size) ++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size) + { + struct peci_adapter *adapter; + +@@ -1263,7 +1355,7 @@ EXPORT_SYMBOL_GPL(peci_alloc_adapter); + + static int peci_register_adapter(struct peci_adapter *adapter) + { +- int rc = -EINVAL; ++ int ret = -EINVAL; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) +@@ -1275,27 +1367,17 @@ static int peci_register_adapter(struct peci_adapter *adapter) + if (WARN(!adapter->xfer, "peci adapter has no xfer function\n")) + goto err_free_idr; + +- rt_mutex_init(&adapter->bus_lock); ++ mutex_init(&adapter->bus_lock); + mutex_init(&adapter->userspace_clients_lock); + INIT_LIST_HEAD(&adapter->userspace_clients); + + dev_set_name(&adapter->dev, "peci-%d", adapter->nr); + +- /* cdev */ +- cdev_init(&adapter->cdev, &peci_fops); +- adapter->cdev.owner = THIS_MODULE; +- adapter->dev.devt = MKDEV(MAJOR(peci_devt), adapter->nr); +- rc = cdev_add(&adapter->cdev, adapter->dev.devt, 1); +- if (rc) { +- pr_err("adapter '%s': can't add cdev (%d)\n", +- adapter->name, rc); +- goto err_free_idr; +- } +- rc = device_add(&adapter->dev); +- if (rc) { ++ ret = device_add(&adapter->dev); ++ if (ret) { + pr_err("adapter '%s': can't add device (%d)\n", +- adapter->name, rc); +- goto err_del_cdev; ++ adapter->name, ret); ++ goto err_free_idr; + } + + dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name); +@@ -1309,13 +1391,11 @@ static int peci_register_adapter(struct peci_adapter *adapter) + + return 0; + +-err_del_cdev: +- cdev_del(&adapter->cdev); + err_free_idr: + mutex_lock(&core_lock); + idr_remove(&peci_adapter_idr, adapter->nr); + mutex_unlock(&core_lock); +- return rc; ++ return ret; + } + + static int peci_add_numbered_adapter(struct peci_adapter *adapter) +@@ -1411,7 +1491,7 @@ void peci_del_adapter(struct peci_adapter *adapter) + } + mutex_unlock(&adapter->userspace_clients_lock); + +- /** ++ /* + * Detach any active clients. This can't fail, thus we do not + * check the returned value. + */ +@@ -1420,13 +1500,8 @@ void peci_del_adapter(struct peci_adapter *adapter) + /* device name is gone after device_unregister */ + dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name); + +- /* free cdev */ +- cdev_del(&adapter->cdev); +- + pm_runtime_disable(&adapter->dev); +- + nr = adapter->nr; +- + device_unregister(&adapter->dev); + + /* free bus id */ +@@ -1436,6 +1511,18 @@ void peci_del_adapter(struct peci_adapter *adapter) + } + EXPORT_SYMBOL_GPL(peci_del_adapter); + ++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *)) ++{ ++ int ret; ++ ++ mutex_lock(&core_lock); ++ ret = bus_for_each_dev(&peci_bus_type, NULL, data, fn); ++ mutex_unlock(&core_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(peci_for_each_dev); ++ + /** + * peci_register_driver - register a PECI driver + * @owner: owner module of the driver being registered +@@ -1446,7 +1533,7 @@ EXPORT_SYMBOL_GPL(peci_del_adapter); + */ + int peci_register_driver(struct module *owner, struct peci_driver *driver) + { +- int rc; ++ int ret; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) +@@ -1456,13 +1543,13 @@ int peci_register_driver(struct module *owner, struct peci_driver *driver) + driver->driver.owner = owner; + driver->driver.bus = &peci_bus_type; + +- /** ++ /* + * When registration returns, the driver core + * will have called probe() for all matching-but-unbound devices. + */ +- rc = driver_register(&driver->driver); +- if (rc) +- return rc; ++ ret = driver_register(&driver->driver); ++ if (ret) ++ return ret; + + pr_debug("driver [%s] registered\n", driver->driver.name); + +@@ -1492,13 +1579,6 @@ static int __init peci_init(void) + return ret; + } + +- ret = alloc_chrdev_region(&peci_devt, 0, PECI_CDEV_MAX, "peci"); +- if (ret < 0) { +- pr_err("peci: Failed to allocate chr dev region!\n"); +- bus_unregister(&peci_bus_type); +- return ret; +- } +- + crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL); + + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) +@@ -1514,11 +1594,10 @@ static void __exit peci_exit(void) + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) + WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier)); + +- unregister_chrdev_region(peci_devt, PECI_CDEV_MAX); + bus_unregister(&peci_bus_type); + } + +-postcore_initcall(peci_init); ++subsys_initcall(peci_init); + module_exit(peci_exit); + + MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>"); +diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c +new file mode 100644 +index 000000000000..5de0683206bc +--- /dev/null ++++ b/drivers/peci/peci-dev.c +@@ -0,0 +1,340 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2018-2019 Intel Corporation ++ ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/notifier.h> ++#include <linux/peci.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++/* ++ * A peci_dev represents an peci_adapter ... an PECI or SMBus master, not a ++ * slave (peci_client) with which messages will be exchanged. It's coupled ++ * with a character special file which is accessed by user mode drivers. ++ * ++ * The list of peci_dev structures is parallel to the peci_adapter lists ++ * maintained by the driver model, and is updated using bus notifications. ++ */ ++struct peci_dev { ++ struct list_head list; ++ struct peci_adapter *adapter; ++ struct device *dev; ++ struct cdev cdev; ++}; ++ ++#define PECI_MINORS MINORMASK ++ ++static dev_t peci_devt; ++static LIST_HEAD(peci_dev_list); ++static DEFINE_SPINLOCK(peci_dev_list_lock); ++ ++static struct peci_dev *peci_dev_get_by_minor(uint index) ++{ ++ struct peci_dev *peci_dev; ++ ++ spin_lock(&peci_dev_list_lock); ++ list_for_each_entry(peci_dev, &peci_dev_list, list) { ++ if (peci_dev->adapter->nr == index) ++ goto found; ++ } ++ peci_dev = NULL; ++found: ++ spin_unlock(&peci_dev_list_lock); ++ ++ return peci_dev; ++} ++ ++static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter) ++{ ++ struct peci_dev *peci_dev; ++ ++ if (adapter->nr >= PECI_MINORS) { ++ printk(KERN_ERR "peci-dev: Out of device minors (%d)\n", ++ adapter->nr); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ peci_dev = kzalloc(sizeof(*peci_dev), GFP_KERNEL); ++ if (!peci_dev) ++ return ERR_PTR(-ENOMEM); ++ peci_dev->adapter = adapter; ++ ++ spin_lock(&peci_dev_list_lock); ++ list_add_tail(&peci_dev->list, &peci_dev_list); ++ spin_unlock(&peci_dev_list_lock); ++ ++ return peci_dev; ++} ++ ++static void peci_dev_put(struct peci_dev *peci_dev) ++{ ++ spin_lock(&peci_dev_list_lock); ++ list_del(&peci_dev->list); ++ spin_unlock(&peci_dev_list_lock); ++ kfree(peci_dev); ++} ++ ++static ssize_t name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt)); ++ ++ if (!peci_dev) ++ return -ENODEV; ++ ++ return sprintf(buf, "%s\n", peci_dev->adapter->name); ++} ++static DEVICE_ATTR_RO(name); ++ ++static struct attribute *peci_dev_attrs[] = { ++ &dev_attr_name.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(peci_dev); ++ ++static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg) ++{ ++ struct peci_dev *peci_dev = file->private_data; ++ struct peci_xfer_msg __user *uxmsg; ++ struct peci_xfer_msg *xmsg = NULL; ++ void __user *umsg; ++ enum peci_cmd cmd; ++ u8 *msg = NULL; ++ uint msg_len; ++ int ret; ++ ++ cmd = _IOC_NR(iocmd); ++ msg_len = _IOC_SIZE(iocmd); ++ ++ switch (cmd) { ++ case PECI_CMD_XFER: ++ if (msg_len != sizeof(struct peci_xfer_msg)) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ uxmsg = (struct peci_xfer_msg __user *)arg; ++ xmsg = peci_get_xfer_msg(uxmsg->tx_len, uxmsg->rx_len); ++ if (IS_ERR(xmsg)) { ++ ret = PTR_ERR(xmsg); ++ break; ++ } ++ ++ if (uxmsg->tx_len && ++ copy_from_user(uxmsg->tx_buf, xmsg->tx_buf, ++ uxmsg->tx_len)) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ ret = peci_command(peci_dev->adapter, cmd, xmsg); ++ if (!ret && uxmsg->rx_len && ++ copy_to_user(xmsg->rx_buf, uxmsg->rx_buf, ++ uxmsg->rx_len)) ++ ret = -EFAULT; ++ ++ break; ++ ++ default: ++ umsg = (void __user *)arg; ++ msg = memdup_user(umsg, msg_len); ++ if (IS_ERR(msg)) { ++ ret = PTR_ERR(msg); ++ break; ++ } ++ ++ ret = peci_command(peci_dev->adapter, cmd, msg); ++ if (!ret && copy_to_user(umsg, msg, msg_len)) ++ ret = -EFAULT; ++ ++ break; ++ } ++ ++ peci_put_xfer_msg(xmsg); ++ kfree(msg); ++ ++ return (long)ret; ++} ++ ++static int peci_dev_open(struct inode *inode, struct file *file) ++{ ++ struct peci_adapter *adapter; ++ struct peci_dev *peci_dev; ++ ++ peci_dev = peci_dev_get_by_minor(iminor(inode)); ++ if (!peci_dev) ++ return -ENODEV; ++ ++ adapter = peci_get_adapter(peci_dev->adapter->nr); ++ if (!adapter) ++ return -ENODEV; ++ ++ file->private_data = peci_dev; ++ ++ return 0; ++} ++ ++static int peci_dev_release(struct inode *inode, struct file *file) ++{ ++ struct peci_dev *peci_dev = file->private_data; ++ ++ peci_put_adapter(peci_dev->adapter); ++ file->private_data = NULL; ++ ++ return 0; ++} ++ ++static const struct file_operations peci_dev_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = peci_dev_ioctl, ++ .open = peci_dev_open, ++ .release = peci_dev_release, ++ .llseek = no_llseek, ++}; ++ ++static struct class *peci_dev_class; ++ ++static int peci_dev_attach_adapter(struct device *dev, void *dummy) ++{ ++ struct peci_adapter *adapter; ++ struct peci_dev *peci_dev; ++ dev_t devt; ++ int ret; ++ ++ if (dev->type != &peci_adapter_type) ++ return 0; ++ ++ adapter = to_peci_adapter(dev); ++ peci_dev = peci_dev_alloc(adapter); ++ if (IS_ERR(peci_dev)) ++ return PTR_ERR(peci_dev); ++ ++ cdev_init(&peci_dev->cdev, &peci_dev_fops); ++ peci_dev->cdev.owner = THIS_MODULE; ++ devt = MKDEV(MAJOR(peci_devt), adapter->nr); ++ ++ ret = cdev_add(&peci_dev->cdev, devt, 1); ++ if (ret) ++ goto err_put_dev; ++ ++ /* register this peci device with the driver core */ ++ peci_dev->dev = device_create(peci_dev_class, &adapter->dev, devt, NULL, ++ "peci-%d", adapter->nr); ++ if (IS_ERR(peci_dev->dev)) { ++ ret = PTR_ERR(peci_dev->dev); ++ goto err_del_cdev; ++ } ++ ++ pr_info("peci-dev: adapter [%s] registered as minor %d\n", ++ adapter->name, adapter->nr); ++ ++ return 0; ++ ++err_del_cdev: ++ cdev_del(&peci_dev->cdev); ++err_put_dev: ++ peci_dev_put(peci_dev); ++ ++ return ret; ++} ++ ++static int peci_dev_detach_adapter(struct device *dev, void *dummy) ++{ ++ struct peci_adapter *adapter; ++ struct peci_dev *peci_dev; ++ dev_t devt; ++ ++ if (dev->type != &peci_adapter_type) ++ return 0; ++ ++ adapter = to_peci_adapter(dev); ++ peci_dev = peci_dev_get_by_minor(adapter->nr); ++ if (!peci_dev) ++ return 0; ++ ++ cdev_del(&peci_dev->cdev); ++ devt = peci_dev->dev->devt; ++ peci_dev_put(peci_dev); ++ device_destroy(peci_dev_class, devt); ++ ++ pr_info("peci-dev: adapter [%s] unregistered\n", adapter->name); ++ ++ return 0; ++} ++ ++static int peci_dev_notifier_call(struct notifier_block *nb, ulong action, ++ void *data) ++{ ++ struct device *dev = data; ++ ++ switch (action) { ++ case BUS_NOTIFY_ADD_DEVICE: ++ return peci_dev_attach_adapter(dev, NULL); ++ case BUS_NOTIFY_DEL_DEVICE: ++ return peci_dev_detach_adapter(dev, NULL); ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block peci_dev_notifier = { ++ .notifier_call = peci_dev_notifier_call, ++}; ++ ++static int __init peci_dev_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "peci /dev entries driver\n"); ++ ++ ret = alloc_chrdev_region(&peci_devt, 0, PECI_MINORS, "peci"); ++ if (ret < 0) { ++ pr_err("peci: Failed to allocate chr dev region!\n"); ++ bus_unregister(&peci_bus_type); ++ goto err; ++ } ++ ++ peci_dev_class = class_create(THIS_MODULE, "peci-dev"); ++ if (IS_ERR(peci_dev_class)) { ++ ret = PTR_ERR(peci_dev_class); ++ goto err_unreg_chrdev; ++ } ++ peci_dev_class->dev_groups = peci_dev_groups; ++ ++ /* Keep track of adapters which will be added or removed later */ ++ ret = bus_register_notifier(&peci_bus_type, &peci_dev_notifier); ++ if (ret) ++ goto err_destroy_class; ++ ++ /* Bind to already existing adapters right away */ ++ peci_for_each_dev(NULL, peci_dev_attach_adapter); ++ ++ return 0; ++ ++err_destroy_class: ++ class_destroy(peci_dev_class); ++err_unreg_chrdev: ++ unregister_chrdev_region(peci_devt, PECI_MINORS); ++err: ++ printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); ++ ++ return ret; ++} ++ ++static void __exit peci_dev_exit(void) ++{ ++ bus_unregister_notifier(&peci_bus_type, &peci_dev_notifier); ++ peci_for_each_dev(NULL, peci_dev_detach_adapter); ++ class_destroy(peci_dev_class); ++ unregister_chrdev_region(peci_devt, PECI_MINORS); ++} ++ ++module_init(peci_dev_init); ++module_exit(peci_dev_exit); ++ ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("PECI /dev entries driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h +index 8f6d823a59cd..1f1b07a9aeab 100644 +--- a/include/linux/mfd/intel-peci-client.h ++++ b/include/linux/mfd/intel-peci-client.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H + #define __LINUX_MFD_INTEL_PECI_CLIENT_H +@@ -9,7 +9,7 @@ + #if IS_ENABLED(CONFIG_X86) + #include <asm/intel-family.h> + #else +-/** ++/* + * Architectures other than x86 cannot include the header file so define these + * at here. These are needed for detecting type of client x86 CPUs behind a PECI + * connection. +@@ -58,7 +58,6 @@ struct cpu_gen_info { + /** + * struct peci_client_manager - PECI client manager information + * @client; pointer to the PECI client +- * @dev: pointer to the struct device + * @name: PECI client manager name + * @gen_info: CPU generation info of the detected CPU + * +@@ -67,7 +66,6 @@ struct cpu_gen_info { + */ + struct peci_client_manager { + struct peci_client *client; +- struct device *dev; + char name[PECI_NAME_SIZE]; + const struct cpu_gen_info *gen_info; + }; +diff --git a/include/linux/peci.h b/include/linux/peci.h +index d0e47d45d1d0..6fc424dc2a73 100644 +--- a/include/linux/peci.h ++++ b/include/linux/peci.h +@@ -1,19 +1,18 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __LINUX_PECI_H + #define __LINUX_PECI_H + +-#include <linux/cdev.h> + #include <linux/device.h> ++#include <linux/mutex.h> + #include <linux/peci-ioctl.h> +-#include <linux/rtmutex.h> + + #define PECI_NAME_SIZE 32 + + struct peci_board_info { + char type[PECI_NAME_SIZE]; +- unsigned short addr; /* CPU client address */ ++ u8 addr; /* CPU client address */ + struct device_node *of_node; + }; + +@@ -22,29 +21,29 @@ struct peci_board_info { + * @owner: owner module of the PECI adpater + * @bus_lock: mutex for exclusion of multiple callers + * @dev: device interface to this driver +- * @cdev: character device object to create character device + * @nr: the bus number to map + * @name: name of the adapter + * @userspace_clients_lock: mutex for exclusion of clients handling + * @userspace_clients: list of registered clients + * @xfer: low-level transfer function pointer of the adapter + * @cmd_mask: mask for supportable PECI commands ++ * @use_dma: flag for indicating that adapter uses DMA + * + * Each PECI adapter can communicate with one or more PECI client children. + * These make a small bus, sharing a single wired PECI connection. + */ + struct peci_adapter { + struct module *owner; +- struct rt_mutex bus_lock; ++ struct mutex bus_lock; + struct device dev; +- struct cdev cdev; + int nr; + char name[PECI_NAME_SIZE]; + struct mutex userspace_clients_lock; /* clients list mutex */ + struct list_head userspace_clients; + int (*xfer)(struct peci_adapter *adapter, + struct peci_xfer_msg *msg); +- uint cmd_mask; ++ u32 cmd_mask; ++ bool use_dma; + }; + + static inline struct peci_adapter *to_peci_adapter(void *d) +@@ -87,8 +86,8 @@ static inline struct peci_client *to_peci_client(void *d) + } + + struct peci_device_id { +- char name[PECI_NAME_SIZE]; +- unsigned long driver_data; /* Data private to the driver */ ++ char name[PECI_NAME_SIZE]; ++ ulong driver_data; /* Data private to the driver */ + }; + + /** +@@ -129,13 +128,22 @@ static inline struct peci_driver *to_peci_driver(void *d) + /* use a define to avoid include chaining to get THIS_MODULE */ + #define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver) + ++extern struct bus_type peci_bus_type; ++extern struct device_type peci_adapter_type; ++extern struct device_type peci_client_type; ++ + int peci_register_driver(struct module *owner, struct peci_driver *drv); + void peci_del_driver(struct peci_driver *driver); + struct peci_client *peci_verify_client(struct device *dev); +-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size); ++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size); ++struct peci_adapter *peci_get_adapter(int nr); ++void peci_put_adapter(struct peci_adapter *adapter); + int peci_add_adapter(struct peci_adapter *adapter); + void peci_del_adapter(struct peci_adapter *adapter); + struct peci_adapter *peci_verify_adapter(struct device *dev); ++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *)); ++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len); ++void peci_put_xfer_msg(struct peci_xfer_msg *msg); + int peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg); + int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id); + +diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h +index a6dae71cbff5..8467b2fbee1f 100644 +--- a/include/uapi/linux/peci-ioctl.h ++++ b/include/uapi/linux/peci-ioctl.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __PECI_IOCTL_H + #define __PECI_IOCTL_H +@@ -7,136 +7,34 @@ + #include <linux/ioctl.h> + #include <linux/types.h> + +-/* Base Address of 48d */ +-#define PECI_BASE_ADDR 0x30 /* The PECI client's default address of 0x30 */ +-#define PECI_OFFSET_MAX 8 /* Max numver of CPU clients */ +- +-/* PCI Access */ +-#define MAX_PCI_READ_LEN 24 /* Number of bytes of the PCI Space read */ +- +-#define PCI_BUS0_CPU0 0x00 +-#define PCI_BUS0_CPU1 0x80 +-#define PCI_CPUBUSNO_BUS 0x00 +-#define PCI_CPUBUSNO_DEV 0x08 +-#define PCI_CPUBUSNO_FUNC 0x02 +-#define PCI_CPUBUSNO 0xcc +-#define PCI_CPUBUSNO_1 0xd0 +-#define PCI_CPUBUSNO_VALID 0xd4 +- +-/* Package Identifier Read Parameter Value */ +-#define PKG_ID_CPU_ID 0x0000 /* CPUID Info */ +-#define PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ +-#define PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ +-#define PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ +-#define PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ +-#define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ +- +-/* RdPkgConfig Index */ +-#define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */ +-#define MBX_INDEX_VR_DEBUG 1 /* VR Debug */ +-#define MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */ +-#define MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */ +-#define MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */ +-#define MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */ +-#define MBX_INDEX_EPI 6 /* Efficient Performance Indication */ +-#define MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */ +-#define MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */ +-#define MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */ +-#define MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */ +-#define MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */ +-#define MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */ +-#define MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ +-#define MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */ +-#define MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */ +-#define MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */ +-#define MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */ +-#define MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */ +-#define MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */ +-#define MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */ +-#define MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */ +-#define MBX_INDEX_TDP 28 /* Thermal design power minimum */ +-#define MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */ +-#define MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */ +-#define MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */ +-#define MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */ +-#define MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */ +-#define MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */ +-#define MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */ +-#define MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */ +-#define MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */ +-#define MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */ +-#define MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */ +-#define MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */ +-#define MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */ +-#define MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */ +-#define MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */ +-#define MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */ +-#define MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */ +-#define MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */ +-#define MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */ +-#define MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */ +- +-/* WrPkgConfig Index */ +-#define MBX_INDEX_DIMM_AMBIENT 19 +-#define MBX_INDEX_DIMM_TEMP 24 ++/* The PECI client's default address of 0x30 */ ++#define PECI_BASE_ADDR 0x30 ++ ++/* Max number of CPU clients */ ++#define PECI_OFFSET_MAX 8 ++ ++/* PECI read/write data buffer size max */ ++#define PECI_BUFFER_SIZE 255 + + /* Device Specific Completion Code (CC) Definition */ +-#define DEV_PECI_CC_SUCCESS 0x40 +-#define DEV_PECI_CC_TIMEOUT 0x80 +-#define DEV_PECI_CC_OUT_OF_RESOURCE 0x81 +-#define DEV_PECI_CC_UNAVAIL_RESOURCE 0x82 +-#define DEV_PECI_CC_INVALID_REQ 0x90 ++#define PECI_DEV_CC_SUCCESS 0x40 ++#define PECI_DEV_CC_TIMEOUT 0x80 ++#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81 ++#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82 ++#define PECI_DEV_CC_INVALID_REQ 0x90 + + /* Completion Code mask to check retry needs */ +-#define DEV_PECI_CC_RETRY_CHECK_MASK 0xf0 +-#define DEV_PECI_CC_NEED_RETRY 0x80 ++#define PECI_DEV_CC_RETRY_CHECK_MASK 0xf0 ++#define PECI_DEV_CC_NEED_RETRY 0x80 + + /* Skylake EDS says to retry for 250ms */ +-#define DEV_PECI_RETRY_TIME_MS 250 +-#define DEV_PECI_RETRY_INTERVAL_USEC 10000 +-#define DEV_PECI_RETRY_BIT 0x01 +- +-#define GET_TEMP_WR_LEN 1 +-#define GET_TEMP_RD_LEN 2 +-#define GET_TEMP_PECI_CMD 0x01 +- +-#define GET_DIB_WR_LEN 1 +-#define GET_DIB_RD_LEN 8 +-#define GET_DIB_PECI_CMD 0xf7 +- +-#define RDPKGCFG_WRITE_LEN 5 +-#define RDPKGCFG_READ_LEN_BASE 1 +-#define RDPKGCFG_PECI_CMD 0xa1 +- +-#define WRPKGCFG_WRITE_LEN_BASE 6 +-#define WRPKGCFG_READ_LEN 1 +-#define WRPKGCFG_PECI_CMD 0xa5 +- +-#define RDIAMSR_WRITE_LEN 5 +-#define RDIAMSR_READ_LEN 9 +-#define RDIAMSR_PECI_CMD 0xb1 +- +-#define WRIAMSR_PECI_CMD 0xb5 +- +-#define RDPCICFG_WRITE_LEN 6 +-#define RDPCICFG_READ_LEN 5 +-#define RDPCICFG_PECI_CMD 0x61 ++#define PECI_DEV_RETRY_TIME_MS 250 ++#define PECI_DEV_RETRY_INTERVAL_USEC 10000 ++#define PECI_DEV_RETRY_BIT 0x01 + +-#define WRPCICFG_PECI_CMD 0x65 ++#define PECI_WRIAMSR_CMD 0xb5 + +-#define RDPCICFGLOCAL_WRITE_LEN 5 +-#define RDPCICFGLOCAL_READ_LEN_BASE 1 +-#define RDPCICFGLOCAL_PECI_CMD 0xe1 +- +-#define WRPCICFGLOCAL_WRITE_LEN_BASE 6 +-#define WRPCICFGLOCAL_READ_LEN 1 +-#define WRPCICFGLOCAL_PECI_CMD 0xe5 +- +-#define PECI_BUFFER_SIZE 32 ++#define PECI_WRPCICFG_CMD 0x65 + + /** + * enum peci_cmd - PECI client commands +@@ -186,11 +84,12 @@ enum peci_cmd { + * raw PECI transfer + */ + struct peci_xfer_msg { +- __u8 addr; +- __u8 tx_len; +- __u8 rx_len; +- __u8 tx_buf[PECI_BUFFER_SIZE]; +- __u8 rx_buf[PECI_BUFFER_SIZE]; ++ __u8 addr; ++ __u8 tx_len; ++ __u8 rx_len; ++ __u8 padding; ++ __u8 *tx_buf; ++ __u8 *rx_buf; + } __attribute__((__packed__)); + + /** +@@ -202,7 +101,8 @@ struct peci_xfer_msg { + * powered-off, etc. + */ + struct peci_ping_msg { +- __u8 addr; ++ __u8 addr; ++ __u8 padding[3]; + } __attribute__((__packed__)); + + /** +@@ -216,8 +116,13 @@ struct peci_ping_msg { + * command. + */ + struct peci_get_dib_msg { +- __u8 addr; +- __u64 dib; ++#define PECI_GET_DIB_WR_LEN 1 ++#define PECI_GET_DIB_RD_LEN 8 ++#define PECI_GET_DIB_CMD 0xf7 ++ ++ __u8 addr; ++ __u8 padding[3]; ++ __u64 dib; + } __attribute__((__packed__)); + + /** +@@ -232,8 +137,14 @@ struct peci_get_dib_msg { + * below the maximum processor junction temperature. + */ + struct peci_get_temp_msg { +- __u8 addr; +- __s16 temp_raw; ++#define PECI_GET_TEMP_WR_LEN 1 ++#define PECI_GET_TEMP_RD_LEN 2 ++#define PECI_GET_TEMP_CMD 0x01 ++ ++ __u8 addr; ++ __u8 padding0[3]; ++ __s16 temp_raw; ++ __u8 padding1[2]; + } __attribute__((__packed__)); + + /** +@@ -251,11 +162,72 @@ struct peci_get_temp_msg { + * DIMM temperatures and so on. + */ + struct peci_rd_pkg_cfg_msg { +- __u8 addr; +- __u8 index; +- __u16 param; +- __u8 rx_len; +- __u8 pkg_config[4]; ++#define PECI_RDPKGCFG_WRITE_LEN 5 ++#define PECI_RDPKGCFG_READ_LEN_BASE 1 ++#define PECI_RDPKGCFG_CMD 0xa1 ++ ++ __u8 addr; ++ __u8 index; ++#define PECI_MBX_INDEX_CPU_ID 0 /* Package Identifier Read */ ++#define PECI_MBX_INDEX_VR_DEBUG 1 /* VR Debug */ ++#define PECI_MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */ ++#define PECI_MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */ ++#define PECI_MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */ ++#define PECI_MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */ ++#define PECI_MBX_INDEX_EPI 6 /* Efficient Performance Indication */ ++#define PECI_MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */ ++#define PECI_MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */ ++#define PECI_MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */ ++#define PECI_MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */ ++#define PECI_MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */ ++#define PECI_MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */ ++#define PECI_MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ ++#define PECI_MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */ ++#define PECI_MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */ ++#define PECI_MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */ ++#define PECI_MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */ ++#define PECI_MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */ ++#define PECI_MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */ ++#define PECI_MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */ ++#define PECI_MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */ ++#define PECI_MBX_INDEX_TDP 28 /* Thermal design power minimum */ ++#define PECI_MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */ ++#define PECI_MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */ ++#define PECI_MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */ ++#define PECI_MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */ ++#define PECI_MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */ ++#define PECI_MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */ ++#define PECI_MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */ ++#define PECI_MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */ ++#define PECI_MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */ ++#define PECI_MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */ ++#define PECI_MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */ ++#define PECI_MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */ ++#define PECI_MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */ ++#define PECI_MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */ ++#define PECI_MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */ ++#define PECI_MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */ ++#define PECI_MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */ ++#define PECI_MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */ ++#define PECI_MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */ ++#define PECI_MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */ ++ ++ __u16 param; ++/* When index is PECI_MBX_INDEX_CPU_ID */ ++#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ ++#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ ++#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ ++#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ ++#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ ++#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ ++ ++ __u8 rx_len; ++ __u8 padding[3]; ++ __u8 pkg_config[4]; + } __attribute__((__packed__)); + + /** +@@ -272,11 +244,19 @@ struct peci_rd_pkg_cfg_msg { + * may include power limiting, thermal averaging constant programming and so on. + */ + struct peci_wr_pkg_cfg_msg { +- __u8 addr; +- __u8 index; +- __u16 param; +- __u8 tx_len; +- __u32 value; ++#define PECI_WRPKGCFG_WRITE_LEN_BASE 6 ++#define PECI_WRPKGCFG_READ_LEN 1 ++#define PECI_WRPKGCFG_CMD 0xa5 ++ ++ __u8 addr; ++ __u8 index; ++#define PECI_MBX_INDEX_DIMM_AMBIENT 19 ++#define PECI_MBX_INDEX_DIMM_TEMP 24 ++ ++ __u16 param; ++ __u8 tx_len; ++ __u8 padding[3]; ++ __u32 value; + } __attribute__((__packed__)); + + /** +@@ -290,10 +270,34 @@ struct peci_wr_pkg_cfg_msg { + * (MSRs) defined in the processor's Intel Architecture (IA). + */ + struct peci_rd_ia_msr_msg { +- __u8 addr; +- __u8 thread_id; +- __u16 address; +- __u64 value; ++#define PECI_RDIAMSR_WRITE_LEN 5 ++#define PECI_RDIAMSR_READ_LEN 9 ++#define PECI_RDIAMSR_CMD 0xb1 ++ ++ __u8 addr; ++ __u8 thread_id; ++ __u16 address; ++ __u64 value; ++} __attribute__((__packed__)); ++ ++/** ++ * struct peci_wr_ia_msr_msg - WrIAMSR command ++ * @addr: address of the client ++ * @thread_id: ID of the specific logical processor ++ * @address: address of MSR to write to ++ * @tx_len: number of data to be written in bytes ++ * @value: data to be written ++ * ++ * The WrIAMSR() PECI command provides write access to Model Specific Registers ++ * (MSRs) defined in the processor's Intel Architecture (IA). ++ */ ++struct peci_wr_ia_msr_msg { ++ __u8 addr; ++ __u8 thread_id; ++ __u16 address; ++ __u8 tx_len; ++ __u8 padding[3]; ++ __u64 value; + } __attribute__((__packed__)); + + /** +@@ -310,12 +314,52 @@ struct peci_rd_ia_msr_msg { + * processor. + */ + struct peci_rd_pci_cfg_msg { +- __u8 addr; +- __u8 bus; +- __u8 device; +- __u8 function; +- __u16 reg; +- __u8 pci_config[4]; ++#define PECI_RDPCICFG_WRITE_LEN 6 ++#define PECI_RDPCICFG_READ_LEN 5 ++#define PECI_RDPCICFG_READ_LEN_MAX 24 ++#define PECI_RDPCICFG_CMD 0x61 ++ ++ __u8 addr; ++ __u8 bus; ++#define PECI_PCI_BUS0_CPU0 0x00 ++#define PECI_PCI_BUS0_CPU1 0x80 ++#define PECI_PCI_CPUBUSNO_BUS 0x00 ++#define PECI_PCI_CPUBUSNO_DEV 0x08 ++#define PECI_PCI_CPUBUSNO_FUNC 0x02 ++#define PECI_PCI_CPUBUSNO 0xcc ++#define PECI_PCI_CPUBUSNO_1 0xd0 ++#define PECI_PCI_CPUBUSNO_VALID 0xd4 ++ ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 padding[2]; ++ __u8 pci_config[4]; ++} __attribute__((__packed__)); ++ ++/** ++ * struct peci_wr_pci_cfg_msg - WrPCIConfig command ++ * @addr: address of the client ++ * @bus: PCI bus number ++ * @device: PCI device number ++ * @function: specific function to write to ++ * @reg: specific register to write to ++ * @tx_len: number of data to be written in bytes ++ * @pci_config: config data to be written ++ * ++ * The RdPCIConfig() command provides sideband write access to the PCI ++ * configuration space maintained in downstream devices external to the ++ * processor. ++ */ ++struct peci_wr_pci_cfg_msg { ++ __u8 addr; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 tx_len; ++ __u8 padding; ++ __u8 pci_config[4]; + } __attribute__((__packed__)); + + /** +@@ -333,13 +377,18 @@ struct peci_rd_pci_cfg_msg { + * processor IIO and uncore registers within the PCI configuration space. + */ + struct peci_rd_pci_cfg_local_msg { +- __u8 addr; +- __u8 bus; +- __u8 device; +- __u8 function; +- __u16 reg; +- __u8 rx_len; +- __u8 pci_config[4]; ++#define PECI_RDPCICFGLOCAL_WRITE_LEN 5 ++#define PECI_RDPCICFGLOCAL_READ_LEN_BASE 1 ++#define PECI_RDPCICFGLOCAL_CMD 0xe1 ++ ++ __u8 addr; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 rx_len; ++ __u8 padding[3]; ++ __u8 pci_config[4]; + } __attribute__((__packed__)); + + /** +@@ -357,13 +406,18 @@ struct peci_rd_pci_cfg_local_msg { + * access this space even before BIOS enumeration of the system buses. + */ + struct peci_wr_pci_cfg_local_msg { +- __u8 addr; +- __u8 bus; +- __u8 device; +- __u8 function; +- __u16 reg; +- __u8 tx_len; +- __u32 value; ++#define PECI_WRPCICFGLOCAL_WRITE_LEN_BASE 6 ++#define PECI_WRPCICFGLOCAL_READ_LEN 1 ++#define PECI_WRPCICFGLOCAL_CMD 0xe5 ++ ++ __u8 addr; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 tx_len; ++ __u8 padding[3]; ++ __u32 value; + } __attribute__((__packed__)); + + #define PECI_IOC_BASE 0xb7 +@@ -389,9 +443,15 @@ struct peci_wr_pci_cfg_local_msg { + #define PECI_IOC_RD_IA_MSR \ + _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg) + ++#define PECI_IOC_WR_IA_MSR \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_ia_msr_msg) ++ + #define PECI_IOC_RD_PCI_CFG \ + _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg) + ++#define PECI_IOC_WR_PCI_CFG \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG, struct peci_wr_pci_cfg_msg) ++ + #define PECI_IOC_RD_PCI_CFG_LOCAL \ + _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \ + struct peci_rd_pci_cfg_local_msg) +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch new file mode 100644 index 000000000..391d6f816 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch @@ -0,0 +1,426 @@ +From 59e2471d9bf64fa7d539520ef66cf5f33c0b0e55 Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Tue, 13 Feb 2018 14:28:12 +0800 +Subject: [PATCH] i2c: slave-mqueue: add mqueue driver to receive ipmi message + +Some protocols over I2C are designed for bi-directional transferring +messages by using I2C Master Write protocol. Like the MCTP (Management +Component Transport Protocol) and IPMB (Intelligent Platform Management +Bus), they both require that the userspace can receive messages from +I2C dirvers under slave mode. + +This new slave mqueue backend is used to receive and queue messages, it +will exposes these messages to userspace by sysfs bin file. + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +--- + Documentation/i2c/slave-mqueue-backend.rst | 125 +++++++++++++++++ + drivers/i2c/Kconfig | 23 +++ + drivers/i2c/Makefile | 1 + + drivers/i2c/i2c-slave-mqueue.c | 217 +++++++++++++++++++++++++++++ + 4 files changed, 366 insertions(+) + create mode 100644 Documentation/i2c/slave-mqueue-backend.rst + create mode 100644 drivers/i2c/i2c-slave-mqueue.c + +diff --git a/Documentation/i2c/slave-mqueue-backend.rst b/Documentation/i2c/slave-mqueue-backend.rst +new file mode 100644 +index 000000000000..3966cf0ab8da +--- /dev/null ++++ b/Documentation/i2c/slave-mqueue-backend.rst +@@ -0,0 +1,125 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++===================================== ++Linux I2C slave message queue backend ++===================================== ++ ++:Author: Haiyue Wang <haiyue.wang@linux.intel.com> ++ ++Some protocols over I2C/SMBus are designed for bi-directional transferring ++messages by using I2C Master Write protocol. This requires that both sides ++of the communication have slave addresses. ++ ++Like MCTP (Management Component Transport Protocol) and IPMB (Intelligent ++Platform Management Bus), they both require that the userspace can receive ++messages from i2c dirvers under slave mode. ++ ++This I2C slave mqueue (message queue) backend is used to receive and queue ++messages from the remote i2c intelligent device; and it will add the target ++slave address (with R/W# bit is always 0) into the message at the first byte, ++so that userspace can use this byte to dispatch the messages into different ++handling modules. Also, like IPMB, the address byte is in its message format, ++it needs it to do checksum. ++ ++For messages are time related, so this backend will flush the oldest message ++to queue the newest one. ++ ++Link ++---- ++`Intelligent Platform Management Bus ++Communications Protocol Specification ++<https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmp-spec-v1.0.pdf>`_ ++ ++`Management Component Transport Protocol (MCTP) ++SMBus/I2C Transport Binding Specification ++<https://www.dmtf.org/sites/default/files/standards/documents/DSP0237_1.1.0.pdf>`_ ++ ++How to use ++---------- ++For example, the I2C5 bus has slave address 0x10, the below command will create ++the related message queue interface: ++ ++ echo slave-mqueue 0x1010 > /sys/bus/i2c/devices/i2c-5/new_device ++ ++Then you can dump the messages like this: ++ ++ hexdump -C /sys/bus/i2c/devices/5-1010/slave-mqueue ++ ++Code Example ++------------ ++*Note: call 'lseek' before 'read', this is a requirement from kernfs' design.* ++ ++:: ++ ++ #include <sys/types.h> ++ #include <sys/stat.h> ++ #include <unistd.h> ++ #include <poll.h> ++ #include <time.h> ++ #include <fcntl.h> ++ #include <stdio.h> ++ ++ int main(int argc, char *argv[]) ++ { ++ int i, r; ++ struct pollfd pfd; ++ struct timespec ts; ++ unsigned char data[256]; ++ ++ pfd.fd = open(argv[1], O_RDONLY | O_NONBLOCK); ++ if (pfd.fd < 0) ++ return -1; ++ ++ pfd.events = POLLPRI; ++ ++ while (1) { ++ r = poll(&pfd, 1, 5000); ++ ++ if (r < 0) ++ break; ++ ++ if (r == 0 || !(pfd.revents & POLLPRI)) ++ continue; ++ ++ lseek(pfd.fd, 0, SEEK_SET); ++ r = read(pfd.fd, data, sizeof(data)); ++ if (r <= 0) ++ continue; ++ ++ clock_gettime(CLOCK_MONOTONIC, &ts); ++ printf("[%ld.%.9ld] :", ts.tv_sec, ts.tv_nsec); ++ for (i = 0; i < r; i++) ++ printf(" %02x", data[i]); ++ printf("\n"); ++ } ++ ++ close(pfd.fd); ++ ++ return 0; ++ } ++ ++Result ++------ ++*./a.out "/sys/bus/i2c/devices/5-1010/slave-mqueue"* ++ ++:: ++ ++ [10183.232500449] : 20 18 c8 2c 78 01 5b ++ [10183.479358348] : 20 18 c8 2c 78 01 5b ++ [10183.726556812] : 20 18 c8 2c 78 01 5b ++ [10183.972605863] : 20 18 c8 2c 78 01 5b ++ [10184.220124772] : 20 18 c8 2c 78 01 5b ++ [10184.467764166] : 20 18 c8 2c 78 01 5b ++ [10193.233421784] : 20 18 c8 2c 7c 01 57 ++ [10193.480273460] : 20 18 c8 2c 7c 01 57 ++ [10193.726788733] : 20 18 c8 2c 7c 01 57 ++ [10193.972781945] : 20 18 c8 2c 7c 01 57 ++ [10194.220487360] : 20 18 c8 2c 7c 01 57 ++ [10194.468089259] : 20 18 c8 2c 7c 01 57 ++ [10203.233433099] : 20 18 c8 2c 80 01 53 ++ [10203.481058715] : 20 18 c8 2c 80 01 53 ++ [10203.727610472] : 20 18 c8 2c 80 01 53 ++ [10203.974044856] : 20 18 c8 2c 80 01 53 ++ [10204.220734634] : 20 18 c8 2c 80 01 53 ++ [10204.468461664] : 20 18 c8 2c 80 01 53 ++ +diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig +index efc3354d60ae..04fb851f2c82 100644 +--- a/drivers/i2c/Kconfig ++++ b/drivers/i2c/Kconfig +@@ -118,6 +118,29 @@ if I2C_SLAVE + config I2C_SLAVE_EEPROM + tristate "I2C eeprom slave driver" + ++config I2C_SLAVE_MQUEUE_MESSAGE_SIZE ++ int "The message size of I2C mqueue slave" ++ default 120 ++ ++config I2C_SLAVE_MQUEUE_QUEUE_SIZE ++ int "The queue size of I2C mqueue slave" ++ default 32 ++ help ++ This number MUST be power of 2. ++ ++config I2C_SLAVE_MQUEUE ++ tristate "I2C mqueue (message queue) slave driver" ++ help ++ Some protocols over I2C are designed for bi-directional transferring ++ messages by using I2C Master Write protocol. This driver is used to ++ receive and queue messages from the remote I2C device. ++ ++ Userspace can get the messages by reading sysfs file that this driver ++ exposes. ++ ++ This support is also available as a module. If so, the module will be ++ called i2c-slave-mqueue. ++ + endif + + config I2C_DEBUG_CORE +diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile +index bed6ba63c983..9a31bc75a446 100644 +--- a/drivers/i2c/Makefile ++++ b/drivers/i2c/Makefile +@@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o + obj-y += algos/ busses/ muxes/ + obj-$(CONFIG_I2C_STUB) += i2c-stub.o + obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o ++obj-$(CONFIG_I2C_SLAVE_MQUEUE) += i2c-slave-mqueue.o + + ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +new file mode 100644 +index 000000000000..6014bca0ff2a +--- /dev/null ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -0,0 +1,217 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2017 - 2018, Intel Corporation. ++ ++#include <linux/i2c.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/sysfs.h> ++ ++#define MQ_MSGBUF_SIZE CONFIG_I2C_SLAVE_MQUEUE_MESSAGE_SIZE ++#define MQ_QUEUE_SIZE CONFIG_I2C_SLAVE_MQUEUE_QUEUE_SIZE ++#define MQ_QUEUE_NEXT(x) (((x) + 1) & (MQ_QUEUE_SIZE - 1)) ++ ++struct mq_msg { ++ int len; ++ u8 *buf; ++}; ++ ++struct mq_queue { ++ struct bin_attribute bin; ++ struct kernfs_node *kn; ++ ++ spinlock_t lock; /* spinlock for queue index handling */ ++ int in; ++ int out; ++ ++ struct mq_msg *curr; ++ int truncated; /* drop current if truncated */ ++ struct mq_msg *queue; ++}; ++ ++static int i2c_slave_mqueue_callback(struct i2c_client *client, ++ enum i2c_slave_event event, u8 *val) ++{ ++ struct mq_queue *mq = i2c_get_clientdata(client); ++ struct mq_msg *msg = mq->curr; ++ int ret = 0; ++ ++ switch (event) { ++ case I2C_SLAVE_WRITE_REQUESTED: ++ mq->truncated = 0; ++ ++ msg->len = 1; ++ msg->buf[0] = client->addr << 1; ++ break; ++ ++ case I2C_SLAVE_WRITE_RECEIVED: ++ if (msg->len < MQ_MSGBUF_SIZE) { ++ msg->buf[msg->len++] = *val; ++ } else { ++ dev_err(&client->dev, "message is truncated!\n"); ++ mq->truncated = 1; ++ ret = -EINVAL; ++ } ++ break; ++ ++ case I2C_SLAVE_STOP: ++ if (unlikely(mq->truncated || msg->len < 2)) ++ break; ++ ++ spin_lock(&mq->lock); ++ mq->in = MQ_QUEUE_NEXT(mq->in); ++ mq->curr = &mq->queue[mq->in]; ++ mq->curr->len = 0; ++ ++ /* Flush the oldest message */ ++ if (mq->out == mq->in) ++ mq->out = MQ_QUEUE_NEXT(mq->out); ++ spin_unlock(&mq->lock); ++ ++ kernfs_notify(mq->kn); ++ break; ++ ++ default: ++ *val = 0xFF; ++ break; ++ } ++ ++ return ret; ++} ++ ++static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, ++ struct kobject *kobj, ++ struct bin_attribute *attr, ++ char *buf, loff_t pos, size_t count) ++{ ++ struct mq_queue *mq; ++ struct mq_msg *msg; ++ unsigned long flags; ++ bool more = false; ++ ssize_t ret = 0; ++ ++ mq = dev_get_drvdata(container_of(kobj, struct device, kobj)); ++ ++ spin_lock_irqsave(&mq->lock, flags); ++ if (mq->out != mq->in) { ++ msg = &mq->queue[mq->out]; ++ ++ if (msg->len <= count) { ++ ret = msg->len; ++ memcpy(buf, msg->buf, ret); ++ } else { ++ ret = -EOVERFLOW; /* Drop this HUGE one. */ ++ } ++ ++ mq->out = MQ_QUEUE_NEXT(mq->out); ++ if (mq->out != mq->in) ++ more = true; ++ } ++ spin_unlock_irqrestore(&mq->lock, flags); ++ ++ if (more) ++ kernfs_notify(mq->kn); ++ ++ return ret; ++} ++ ++static int i2c_slave_mqueue_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct mq_queue *mq; ++ int ret, i; ++ void *buf; ++ ++ mq = devm_kzalloc(dev, sizeof(*mq), GFP_KERNEL); ++ if (!mq) ++ return -ENOMEM; ++ ++ BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); ++ ++ buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mq->queue = devm_kzalloc(dev, sizeof(*mq->queue) * MQ_QUEUE_SIZE, ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ for (i = 0; i < MQ_QUEUE_SIZE; i++) ++ mq->queue[i].buf = buf + i * MQ_MSGBUF_SIZE; ++ ++ i2c_set_clientdata(client, mq); ++ ++ spin_lock_init(&mq->lock); ++ mq->curr = &mq->queue[0]; ++ ++ sysfs_bin_attr_init(&mq->bin); ++ mq->bin.attr.name = "slave-mqueue"; ++ mq->bin.attr.mode = 0400; ++ mq->bin.read = i2c_slave_mqueue_bin_read; ++ mq->bin.size = MQ_MSGBUF_SIZE * MQ_QUEUE_SIZE; ++ ++ ret = sysfs_create_bin_file(&dev->kobj, &mq->bin); ++ if (ret) ++ return ret; ++ ++ mq->kn = kernfs_find_and_get(dev->kobj.sd, mq->bin.attr.name); ++ if (!mq->kn) { ++ sysfs_remove_bin_file(&dev->kobj, &mq->bin); ++ return -EFAULT; ++ } ++ ++ ret = i2c_slave_register(client, i2c_slave_mqueue_callback); ++ if (ret) { ++ kernfs_put(mq->kn); ++ sysfs_remove_bin_file(&dev->kobj, &mq->bin); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int i2c_slave_mqueue_remove(struct i2c_client *client) ++{ ++ struct mq_queue *mq = i2c_get_clientdata(client); ++ ++ i2c_slave_unregister(client); ++ ++ kernfs_put(mq->kn); ++ sysfs_remove_bin_file(&client->dev.kobj, &mq->bin); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id i2c_slave_mqueue_id[] = { ++ { "slave-mqueue", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, i2c_slave_mqueue_id); ++ ++#if IS_ENABLED(CONFIG_OF) ++static const struct of_device_id i2c_slave_mqueue_of_match[] = { ++ { .compatible = "slave-mqueue", .data = (void *)0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, i2c_slave_mqueue_of_match); ++#endif ++ ++static struct i2c_driver i2c_slave_mqueue_driver = { ++ .driver = { ++ .name = "i2c-slave-mqueue", ++ .of_match_table = of_match_ptr(i2c_slave_mqueue_of_match), ++ }, ++ .probe = i2c_slave_mqueue_probe, ++ .remove = i2c_slave_mqueue_remove, ++ .id_table = i2c_slave_mqueue_id, ++}; ++module_i2c_driver(i2c_slave_mqueue_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); ++MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch new file mode 100644 index 000000000..95302aae8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch @@ -0,0 +1,623 @@ +From 7d5cd323d3b05a00f8b8a6eb38a5a1ec7925660a Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@intel.com> +Date: Mon, 13 Nov 2017 16:29:44 +0800 +Subject: [PATCH] Aspeed LPC SIO driver + +Add lpc sio device driver for AST2500/2400 + +Signed-off-by: Yong Li <yong.b.li@intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../devicetree/bindings/misc/aspeed-sio.txt | 18 + + arch/arm/boot/dts/aspeed-g4.dtsi | 7 + + arch/arm/boot/dts/aspeed-g5.dtsi | 7 + + drivers/misc/Kconfig | 9 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-lpc-sio.c | 450 +++++++++++++++++++++ + include/uapi/linux/aspeed-lpc-sio.h | 44 ++ + 7 files changed, 536 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/aspeed-sio.txt + create mode 100644 drivers/misc/aspeed-lpc-sio.c + create mode 100644 include/uapi/linux/aspeed-lpc-sio.h + +diff --git a/Documentation/devicetree/bindings/misc/aspeed-sio.txt b/Documentation/devicetree/bindings/misc/aspeed-sio.txt +new file mode 100644 +index 000000000000..3530c2b02f5c +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/aspeed-sio.txt +@@ -0,0 +1,18 @@ ++* Aspeed LPC SIO driver. ++ ++Required properties: ++- compatible : Should be one of: ++ "aspeed,ast2400-lpc-sio" ++ "aspeed,ast2500-lpc-sio" ++- reg : Should contain lpc-sio registers location and length ++- clocks: contains a phandle to the syscon node describing the clocks. ++ There should then be one cell representing the clock to use. ++ ++Example: ++lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++}; ++ +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index e8bcfc90bf7c..a87fd5ee1c84 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -340,6 +340,13 @@ + compatible = "aspeed,bmc-misc"; + }; + ++ lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2400-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ + mbox: mbox@180 { + compatible = "aspeed,ast2400-mbox"; + reg = <0x180 0x5c>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index e5c0ba0f87c8..a568699c28f4 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -451,6 +451,13 @@ + compatible = "aspeed,bmc-misc"; + }; + ++ lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ + mbox: mbox@180 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x180 0x5c>; +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 00d1c547ece7..3ffb18f915e8 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -493,6 +493,15 @@ config ASPEED_LPC_CTRL + ioctl()s, the driver also provides a read/write interface to a BMC ram + region where the host LPC read/write region can be buffered. + ++config ASPEED_LPC_SIO ++ depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON ++ tristate "Aspeed ast2400/2500 HOST LPC SIO support" ++ help ++ Provides a driver to control the LPC SIO interface ++ on ASPEED platform ++ through ++ ioctl()s. ++ + config ASPEED_LPC_SNOOP + tristate "Aspeed ast2500 HOST LPC snoop support" + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 768278b059c3..de2d5c6d186c 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o ++obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-$(CONFIG_MISC_RTSX) += cardreader/ +diff --git a/drivers/misc/aspeed-lpc-sio.c b/drivers/misc/aspeed-lpc-sio.c +new file mode 100644 +index 000000000000..c717a3182320 +--- /dev/null ++++ b/drivers/misc/aspeed-lpc-sio.c +@@ -0,0 +1,450 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2017-2019 Intel Corporation ++ ++#include <linux/clk.h> ++#include <linux/mfd/syscon.h> ++#include <linux/miscdevice.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/regmap.h> ++ ++#include <linux/aspeed-lpc-sio.h> ++ ++#define SOC_NAME "aspeed" ++#define DEVICE_NAME "lpc-sio" ++ ++#define AST_LPC_SWCR0300 0x0 ++#define LPC_PWRGD_STS (1 << 30) ++#define LPC_PWRGD_RISING_EVT_STS (1 << 29) ++#define LPC_PWRGD_FALLING_EVT_STS (1 << 28) ++#define LPC_PWRBTN_STS (1 << 27) ++#define LPC_PWRBTN_RISING_EVT_STS (1 << 26) ++#define LPC_PWRBTN_FALLING_EVT_STS (1 << 25) ++#define LPC_S5N_STS (1 << 21) ++#define LPC_S5N_RISING_EVT_STS (1 << 20) ++#define LPC_S5N_FALLING_EVT_STS (1 << 19) ++#define LPC_S3N_STS (1 << 18) ++#define LPC_S3N_RISING_EVT_STS (1 << 17) ++#define LPC_S3N_FALLING_EVT_STS (1 << 16) ++#define LPC_PWBTO_RAW_STS (1 << 15) ++#define LPC_LAST_ONCTL_STS (1 << 14) ++#define LPC_WAS_PFAIL_STS (1 << 13) ++#define LPC_POWER_UP_FAIL_STS (1 << 12) /* Crowbar */ ++#define LPC_PWRBTN_OVERRIDE_STS (1 << 11) ++ ++#define AST_LPC_SWCR0704 0x4 ++ ++#define AST_LPC_SWCR0B08 0x8 ++#define LPC_PWREQ_OUTPUT_LEVEL (1 << 25) ++#define LPC_PWBTO_OUTPUT_LEVEL (1 << 24) ++#define LPC_ONCTL_STS (1 << 15) ++#define LPC_ONCTL_GPIO_LEVEL (1 << 14) ++#define LPC_ONCTL_EN_GPIO_OUTPUT (1 << 13) ++#define LPC_ONCTL_EN_GPIO_MODE (1 << 12) ++ ++#define AST_LPC_SWCR0F0C 0xC ++#define AST_LPC_SWCR1310 0x10 ++#define AST_LPC_SWCR1714 0x14 ++#define AST_LPC_SWCR1B18 0x18 ++#define AST_LPC_SWCR1F1C 0x1C ++#define AST_LPC_ACPIE3E0 0x20 ++#define AST_LPC_ACPIC1C0 0x24 ++#define AST_LPC_ACPIB3B0 0x28 ++#define AST_LPC_ACPIB7B4 0x2C ++ ++struct aspeed_lpc_sio { ++ struct miscdevice miscdev; ++ struct regmap *regmap; ++ struct clk *clk; ++ struct semaphore lock; ++ unsigned int reg_base; ++}; ++ ++static struct aspeed_lpc_sio *file_aspeed_lpc_sio(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_lpc_sio, ++ miscdev); ++} ++ ++static int aspeed_lpc_sio_open(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++#define LPC_SLP3N5N_EVENT_STATUS (\ ++ LPC_S5N_RISING_EVT_STS | \ ++ LPC_S5N_FALLING_EVT_STS | \ ++ LPC_S3N_RISING_EVT_STS | \ ++ LPC_S3N_FALLING_EVT_STS) ++/************************************* ++ * SLPS3n SLPS5n State ++ * --------------------------------- ++ * 1 1 S12 ++ * 0 1 S3I ++ * x 0 S45 ++ ************************************* ++ */ ++ ++static long sio_get_acpi_state(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* update the ACPI state event status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_SLP3N5N_EVENT_STATUS) { ++ sio_data->param = 1; ++ rc = regmap_write(lpc_sio->regmap, reg, ++ LPC_SLP3N5N_EVENT_STATUS); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", ++ rc, reg); ++ return rc; ++ } ++ } else { ++ sio_data->param = 0; ++ } ++ } ++ ++ if ((val & LPC_S3N_STS) && (val & LPC_S5N_STS)) ++ sio_data->data = ACPI_STATE_S12; ++ else if ((val & LPC_S3N_STS) == 0 && (val & LPC_S5N_STS)) ++ sio_data->data = ACPI_STATE_S3I; ++ else ++ sio_data->data = ACPI_STATE_S45; ++ ++ return 0; ++} ++ ++#define LPC_PWRGD_EVENT_STATUS ( \ ++ LPC_PWRGD_RISING_EVT_STS | \ ++ LPC_PWRGD_FALLING_EVT_STS) ++ ++static long sio_get_pwrgd_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* update the PWRGD event status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_PWRGD_EVENT_STATUS) { ++ sio_data->param = 1; ++ rc = regmap_write(lpc_sio->regmap, reg, ++ LPC_PWRGD_EVENT_STATUS); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", ++ rc, reg); ++ return rc; ++ } ++ } else { ++ sio_data->param = 0; ++ } ++ } ++ ++ sio_data->data = (val & LPC_PWRGD_STS) != 0 ? 1 : 0; ++ ++ return 0; ++} ++ ++static long sio_get_onctl_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ sio_data->data = (val & LPC_ONCTL_STS) != 0 ? 1 : 0; ++ ++ return 0; ++} ++ ++static long sio_set_onctl_gpio(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* Enable ONCTL GPIO mode */ ++ if (sio_data->param != 0) { ++ val |= LPC_ONCTL_EN_GPIO_MODE; ++ val |= LPC_ONCTL_EN_GPIO_OUTPUT; ++ ++ if (sio_data->data != 0) ++ val |= LPC_ONCTL_GPIO_LEVEL; ++ else ++ val &= ~LPC_ONCTL_GPIO_LEVEL; ++ ++ rc = regmap_write(lpc_sio->regmap, reg, val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ } else { ++ val &= ~LPC_ONCTL_EN_GPIO_MODE; ++ rc = regmap_write(lpc_sio->regmap, reg, val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++static long sio_get_pwrbtn_override(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* clear the PWRBTN OVERRIDE status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_PWRBTN_OVERRIDE_STS) { ++ rc = regmap_write(lpc_sio->regmap, reg, ++ LPC_PWRBTN_OVERRIDE_STS); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", ++ rc, reg); ++ return rc; ++ } ++ } ++ } ++ ++ sio_data->data = (val & LPC_PWRBTN_OVERRIDE_STS) != 0 ? 1 : 0; ++ ++ return 0; ++} ++ ++static long sio_get_pfail_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* [ASPEED]: SWCR_03_00[13] (Was_pfail: default 1) is used to identify ++ * this current booting is from AC loss (not DC loss) if FW cleans this ++ * bit after booting successfully every time. ++ **********************************************************************/ ++ if (val & LPC_WAS_PFAIL_STS) { ++ rc = regmap_write(lpc_sio->regmap, reg, 0); /* W0C */ ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ sio_data->data = 1; ++ } else { ++ sio_data->data = 0; ++ } ++ ++ return 0; ++} ++ ++typedef long (*sio_cmd_fn) (struct aspeed_lpc_sio *sio_dev, ++ struct sio_ioctl_data *sio_data); ++static sio_cmd_fn sio_cmd_handle[SIO_MAX_CMD] = { ++ [SIO_GET_ACPI_STATE] = sio_get_acpi_state, ++ [SIO_GET_PWRGD_STATUS] = sio_get_pwrgd_status, ++ [SIO_GET_ONCTL_STATUS] = sio_get_onctl_status, ++ [SIO_SET_ONCTL_GPIO] = sio_set_onctl_gpio, ++ [SIO_GET_PWRBTN_OVERRIDE] = sio_get_pwrbtn_override, ++ [SIO_GET_PFAIL_STATUS] = sio_get_pfail_status, ++}; ++ ++static long aspeed_lpc_sio_ioctl(struct file *file, unsigned int cmd, ++ unsigned long param) ++{ ++ struct aspeed_lpc_sio *lpc_sio = file_aspeed_lpc_sio(file); ++ long ret; ++ sio_cmd_fn cmd_fn; ++ struct sio_ioctl_data sio_data; ++ ++ ++ if (copy_from_user(&sio_data, (void __user *)param, sizeof(sio_data))) ++ return -EFAULT; ++ ++ if (cmd != SIO_IOC_COMMAND || sio_data.sio_cmd >= SIO_MAX_CMD) ++ return -EINVAL; ++ ++ cmd_fn = sio_cmd_handle[sio_data.sio_cmd]; ++ if (cmd_fn == NULL) ++ return -EINVAL; ++ ++ if (down_interruptible(&lpc_sio->lock) != 0) ++ return -ERESTARTSYS; ++ ++ ret = cmd_fn(lpc_sio, &sio_data); ++ if (ret == 0) { ++ if (copy_to_user((void __user *)param, &sio_data, ++ sizeof(sio_data))) ++ ret = -EFAULT; ++ } ++ ++ up(&lpc_sio->lock); ++ ++ return ret; ++} ++ ++static const struct file_operations aspeed_lpc_sio_fops = { ++ .owner = THIS_MODULE, ++ .open = aspeed_lpc_sio_open, ++ .unlocked_ioctl = aspeed_lpc_sio_ioctl, ++}; ++ ++static int aspeed_lpc_sio_probe(struct platform_device *pdev) ++{ ++ struct aspeed_lpc_sio *lpc_sio; ++ struct device *dev; ++ int rc; ++ ++ dev = &pdev->dev; ++ ++ lpc_sio = devm_kzalloc(dev, sizeof(*lpc_sio), GFP_KERNEL); ++ if (!lpc_sio) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, lpc_sio); ++ ++ rc = of_property_read_u32(dev->of_node, "reg", &lpc_sio->reg_base); ++ if (rc) { ++ dev_err(dev, "Couldn't read reg device-tree property\n"); ++ return rc; ++ } ++ ++ lpc_sio->regmap = syscon_node_to_regmap( ++ pdev->dev.parent->of_node); ++ if (IS_ERR(lpc_sio->regmap)) { ++ dev_err(dev, "Couldn't get regmap\n"); ++ return -ENODEV; ++ } ++ ++ sema_init(&lpc_sio->lock, 1); ++ ++ lpc_sio->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(lpc_sio->clk)) { ++ rc = PTR_ERR(lpc_sio->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(lpc_sio->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ ++ lpc_sio->miscdev.minor = MISC_DYNAMIC_MINOR; ++ lpc_sio->miscdev.name = DEVICE_NAME; ++ lpc_sio->miscdev.fops = &aspeed_lpc_sio_fops; ++ lpc_sio->miscdev.parent = dev; ++ rc = misc_register(&lpc_sio->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ goto err; ++ } ++ ++ dev_info(dev, "Loaded at %pap (0x%08x)\n", &lpc_sio->regmap, ++ lpc_sio->reg_base); ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(lpc_sio->clk); ++ ++ return rc; ++} ++ ++static int aspeed_lpc_sio_remove(struct platform_device *pdev) ++{ ++ struct aspeed_lpc_sio *lpc_sio = dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&lpc_sio->miscdev); ++ clk_disable_unprepare(lpc_sio->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_lpc_sio_match[] = { ++ { .compatible = "aspeed,ast2500-lpc-sio" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_lpc_sio_match); ++ ++static struct platform_driver aspeed_lpc_sio_driver = { ++ .driver = { ++ .name = SOC_NAME "-" DEVICE_NAME, ++ .of_match_table = aspeed_lpc_sio_match, ++ }, ++ .probe = aspeed_lpc_sio_probe, ++ .remove = aspeed_lpc_sio_remove, ++}; ++module_platform_driver(aspeed_lpc_sio_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Yong Li <yong.blli@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED AST LPC SIO device driver"); +diff --git a/include/uapi/linux/aspeed-lpc-sio.h b/include/uapi/linux/aspeed-lpc-sio.h +new file mode 100644 +index 000000000000..5dc1efd4a426 +--- /dev/null ++++ b/include/uapi/linux/aspeed-lpc-sio.h +@@ -0,0 +1,44 @@ ++/* ++ * Copyright (C) 2012-2020 ASPEED Technology Inc. ++ * Copyright (c) 2017 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#ifndef _UAPI_LINUX_ASPEED_LPC_SIO_H ++#define _UAPI_LINUX_ASPEED_LPC_SIO_H ++ ++#include <linux/ioctl.h> ++ ++enum ACPI_SLP_STATE { ++ ACPI_STATE_S12 = 1, ++ ACPI_STATE_S3I, ++ ACPI_STATE_S45 ++}; ++ ++/* SWC & ACPI for SuperIO IOCTL */ ++enum SIO_CMD { ++ SIO_GET_ACPI_STATE = 0, ++ SIO_GET_PWRGD_STATUS, ++ SIO_GET_ONCTL_STATUS, ++ SIO_SET_ONCTL_GPIO, ++ SIO_GET_PWRBTN_OVERRIDE, ++ SIO_GET_PFAIL_STATUS, /* Start from AC Loss */ ++ ++ SIO_MAX_CMD ++}; ++ ++struct sio_ioctl_data { ++ unsigned short sio_cmd; ++ unsigned short param; ++ unsigned int data; ++}; ++ ++#define SIO_IOC_BASE 'P' ++#define SIO_IOC_COMMAND _IOWR(SIO_IOC_BASE, 1, struct sio_ioctl_data) ++ ++#endif /* _UAPI_LINUX_ASPEED_LPC_SIO_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch new file mode 100644 index 000000000..120adbbc8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch @@ -0,0 +1,597 @@ +From a01815b4bb983ede71993d6c761dedd22d148b6b Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Sat, 24 Feb 2018 11:12:32 +0800 +Subject: [PATCH] eSPI: add ASPEED AST2500 eSPI driver to boot a host with PCH + runs on eSPI + +When PCH works under eSPI mode, the PMC (Power Management Controller) in +PCH is waiting for SUS_ACK from BMC after it alerts SUS_WARN. It is in +dead loop if no SUS_ACK assert. This is the basic requirement for the BMC +works as eSPI slave. + +Also for the host power on / off actions, from BMC side, the following VW +(Virtual Wire) messages are done in firmware: +1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS +2. SUS_ACK +3. OOB_RESET_ACK +4. HOST_RESET_ACK + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +--- + .../devicetree/bindings/misc/aspeed,espi-slave.txt | 20 ++ + Documentation/misc-devices/espi-slave.rst | 119 +++++++ + arch/arm/boot/dts/aspeed-g5.dtsi | 4 + + drivers/misc/Kconfig | 8 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-espi-slave.c | 353 +++++++++++++++++++++ + 6 files changed, 505 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt + create mode 100644 Documentation/misc-devices/espi-slave.rst + create mode 100644 drivers/misc/aspeed-espi-slave.c + +diff --git a/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt +new file mode 100644 +index 000000000000..4f5d47ecc882 +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt +@@ -0,0 +1,20 @@ ++ASPEED eSPI Slave Controller ++ ++Required properties: ++ - compatible: must be one of: ++ - "aspeed,ast2500-espi-slave" ++ ++ - reg: physical base address of the controller and length of memory mapped ++ region ++ ++ - interrupts: interrupt generated by the controller ++ ++Example: ++ ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2500-espi-slave"; ++ reg = <0x1e6ee000 0x100>; ++ interrupts = <23>; ++ status = "disabled"; ++}; ++ +diff --git a/Documentation/misc-devices/espi-slave.rst b/Documentation/misc-devices/espi-slave.rst +new file mode 100644 +index 000000000000..185acd71bd26 +--- /dev/null ++++ b/Documentation/misc-devices/espi-slave.rst +@@ -0,0 +1,119 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++========== ++eSPI Slave ++========== ++ ++:Author: Haiyue Wang <haiyue.wang@linux.intel.com> ++ ++The PCH (**eSPI master**) provides the eSPI to support connection of a ++BMC (**eSPI slave**) to the platform. ++ ++The LPC and eSPI interfaces are mutually exclusive. Both use the same ++pins, but on power-up, a HW strap determines if the eSPI or the LPC bus ++is operational. Once selected, it’s not possible to change to the other ++interface. ++ ++``eSPI Channels and Supported Transactions`` ++ +------+---------------------+----------------------+--------------------+ ++ | CH # | Channel | Posted Cycles | Non-Posted Cycles | ++ +======+=====================+======================+====================+ ++ | 0 | Peripheral | Memory Write, | Memory Read, | ++ | | | Completions | I/O Read/Write | ++ +------+---------------------+----------------------+--------------------+ ++ | 1 | Virtual Wire | Virtual Wire GET/PUT | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ | 2 | Out-of-Band Message | SMBus Packet GET/PUT | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ | 3 | Flash Access | N/A | Flash Read, Write, | ++ | | | | Erase | ++ +------+---------------------+----------------------+--------------------+ ++ | N/A | General | Register Accesses | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ ++Virtual Wire Channel (Channel 1) Overview ++----------------------------------------- ++ ++The Virtual Wire channel uses a standard message format to communicate ++several types of signals between the components on the platform:: ++ ++ - Sideband and GPIO Pins: System events and other dedicated signals ++ between the PCH and eSPI slave. These signals are tunneled between the ++ two components over eSPI. ++ ++ - Serial IRQ Interrupts: Interrupts are tunneled from the eSPI slave to ++ the PCH. Both edge and triggered interrupts are supported. ++ ++When PCH runs on eSPI mode, from BMC side, the following VW messages are ++done in firmware:: ++ ++ 1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS ++ 2. SUS_ACK ++ 3. OOB_RESET_ACK ++ 4. HOST_RESET_ACK ++ ++``eSPI Virtual Wires (VW)`` ++ +----------------------+---------+---------------------------------------+ ++ |Virtual Wire |PCH Pin |Comments | ++ | |Direction| | ++ +======================+=========+=======================================+ ++ |SUS_WARN# |Output |PCH pin is a GPIO when eSPI is enabled.| ++ | | |eSPI controller receives as VW message.| ++ +----------------------+---------+---------------------------------------+ ++ |SUS_ACK# |Input |PCH pin is a GPIO when eSPI is enabled.| ++ | | |eSPI controller receives as VW message.| ++ +----------------------+---------+---------------------------------------+ ++ |SLAVE_BOOT_LOAD_DONE |Input |Sent when the BMC has completed its | ++ | | |boot process as an indication to | ++ | | |eSPI-MC to continue with the G3 to S0 | ++ | | |exit. | ++ | | |The eSPI Master waits for the assertion| ++ | | |of this virtual wire before proceeding | ++ | | |with the SLP_S5# deassertion. | ++ | | |The intent is that it is never changed | ++ | | |except on a G3 exit - it is reset on a | ++ | | |G3 entry. | ++ +----------------------+---------+---------------------------------------+ ++ |SLAVE_BOOT_LOAD_STATUS|Input |Sent upon completion of the Slave Boot | ++ | | |Load from the attached flash. A stat of| ++ | | |1 indicates that the boot code load was| ++ | | |successful and that the integrity of | ++ | | |the image is intact. | ++ +----------------------+---------+---------------------------------------+ ++ |HOST_RESET_WARN |Output |Sent from the MC just before the Host | ++ | | |is about to enter reset. Upon receiving| ++ | | |, the BMC must flush and quiesce its | ++ | | |upstream Peripheral Channel request | ++ | | |queues and assert HOST_RESET_ACK VWire.| ++ | | |The MC subsequently completes any | ++ | | |outstanding posted transactions or | ++ | | |completions and then disables the | ++ | | |Peripheral Channel via a write to | ++ | | |the Slave's Configuration Register. | ++ +----------------------+---------+---------------------------------------+ ++ |HOST_RESET_ACK |Input |ACK for the HOST_RESET_WARN message | ++ +----------------------+---------+---------------------------------------+ ++ |OOB_RESET_WARN |Output |Sent from the MC just before the OOB | ++ | | |processor is about to enter reset. Upon| ++ | | |receiving, the BMC must flush and | ++ | | |quiesce its OOB Channel upstream | ++ | | |request queues and assert OOB_RESET_ACK| ++ | | |VWire. The-MC subsequently completes | ++ | | |any outstanding posted transactions or | ++ | | |completions and then disables the OOB | ++ | | |Channel via a write to the Slave's | ++ | | |Configuration Register. | ++ +----------------------+---------+---------------------------------------+ ++ |OOB_RESET_ACK |Input |ACK for OOB_RESET_WARN message | ++ +----------------------+---------+---------------------------------------+ ++ ++`Intel C620 Series Chipset Platform Controller Hub ++<https://www.intel.com/content/www/us/en/chipsets/c620-series-chipset-datasheet.html>`_ ++ ++ -- 17. Enhanced Serial Peripheral Interface ++ ++ ++`Enhanced Serial Peripheral Interface (eSPI) ++- Interface Base Specification (for Client and Server Platforms) ++<https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0.pdf>`_ ++ +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 4a302d745b09..165a2bddc6cd 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -266,6 +266,7 @@ + clocks = <&syscon ASPEED_CLK_APB>; + interrupt-controller; + #interrupt-cells = <2>; ++ status = "disabled"; + }; + + sgpio: sgpio@1e780200 { +@@ -360,6 +361,9 @@ + reg = <0x1e6ee000 0x100>; + interrupts = <23>; + status = "disabled"; ++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_espi_default>; + }; + + lpc: lpc@1e789000 { +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index fe1e2a4072a8..f2062546250c 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG + bus. System Configuration interface is one of the possible means + of generating transactions on this bus. + ++config ASPEED_ESPI_SLAVE ++ depends on ARCH_ASPEED || COMPILE_TEST ++ depends on REGMAP_MMIO ++ tristate "Aspeed ast2500 eSPI slave device driver" ++ ---help--- ++ Control Aspeed ast2500 eSPI slave controller to handle event ++ which needs the firmware's processing. ++ + config ASPEED_LPC_CTRL + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON + tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index a2b85ec21d09..bb89694e6b4b 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/ + obj-$(CONFIG_ECHO) += echo/ + obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o + obj-$(CONFIG_CXL_BASE) += cxl/ ++obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o +diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c +new file mode 100644 +index 000000000000..36ae867ca6f9 +--- /dev/null ++++ b/drivers/misc/aspeed-espi-slave.c +@@ -0,0 +1,353 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2012-2015, ASPEED Technology Inc. ++ * Copyright (c) 2015-2018, Intel Corporation. ++ */ ++ ++#include <linux/atomic.h> ++#include <linux/clk.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/regmap.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/timer.h> ++ ++#define DEVICE_NAME "aspeed-espi-slave" ++ ++#define ESPI_CTRL 0x00 ++#define ESPI_CTRL_SW_RESET GENMASK(31, 24) ++#define ESPI_CTRL_OOB_CHRDY BIT(4) ++#define ESPI_ISR 0x08 ++#define ESPI_ISR_HW_RESET BIT(31) ++#define ESPI_ISR_VW_SYS_EVT1 BIT(22) ++#define ESPI_ISR_VW_SYS_EVT BIT(8) ++#define ESPI_IER 0x0C ++#define ESPI_DATA_PORT 0x28 ++#define ESPI_DATA_PORT_ASPEED 0xa8 ++#define ESPI_SYS_IER 0x94 ++#define ESPI_SYS_EVENT 0x98 ++#define ESPI_SYS_INT_T0 0x110 ++#define ESPI_SYS_INT_T1 0x114 ++#define ESPI_SYS_INT_T2 0x118 ++#define ESPI_SYS_ISR 0x11C ++#define ESPI_SYSEVT_HOST_RST_ACK BIT(27) ++#define ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23) ++#define ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20) ++#define ESPI_SYSEVT_OOB_RST_ACK BIT(16) ++#define ESPI_SYSEVT_HOST_RST_WARN BIT(8) ++#define ESPI_SYSEVT_OOB_RST_WARN BIT(6) ++#define ESPI_SYSEVT_PLT_RST_N BIT(5) ++#define ESPI_SYS1_IER 0x100 ++#define ESPI_SYS1_EVENT 0x104 ++#define ESPI_SYS1_INT_T0 0x120 ++#define ESPI_SYS1_INT_T1 0x124 ++#define ESPI_SYS1_INT_T2 0x128 ++#define ESPI_SYS1_ISR 0x12C ++#define ESPI_SYSEVT1_SUS_ACK BIT(20) ++#define ESPI_SYSEVT1_SUS_WARN BIT(0) ++ ++struct aspeed_espi_slave_data { ++ struct regmap *map; ++ struct clk *clk; ++}; ++ ++static void aspeed_espi_slave_sys_event(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 sts, evt; ++ ++ if (regmap_read(priv->map, ESPI_SYS_ISR, &sts) != 0 || ++ regmap_read(priv->map, ESPI_SYS_EVENT, &evt) != 0) { ++ dev_err(dev, "regmap_read failed\n"); ++ return; ++ } ++ ++ dev_dbg(dev, "sys: sts = %08x, evt = %08x\n", sts, evt); ++ ++ if ((evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS) == 0) { ++ dev_info(dev, "Setting espi slave boot done\n"); ++ regmap_write(priv->map, ESPI_SYS_EVENT, ++ evt | ESPI_SYSEVT_SLAVE_BOOT_STATUS | ++ ESPI_SYSEVT_SLAVE_BOOT_DONE); ++ } ++#if 0 ++ if (sts & ESPI_SYSEVT_HOST_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_HOST_RST_WARN; %s ack\n", ++ (evt & ESPI_SYSEVT_HOST_RST_WARN ? "send" : "clr")); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_HOST_RST_ACK, ++ evt & ESPI_SYSEVT_HOST_RST_WARN ? ++ ESPI_SYSEVT_HOST_RST_ACK : 0); ++ } ++ if (sts & ESPI_SYSEVT_OOB_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; %s ack\n", ++ (evt & ESPI_SYSEVT_OOB_RST_WARN ? "send" : "clr")); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_OOB_RST_ACK, ++ evt & ESPI_SYSEVT_OOB_RST_WARN ? ++ ESPI_SYSEVT_OOB_RST_ACK : 0); ++ } ++#else ++ if (sts & ESPI_SYSEVT_HOST_RST_WARN) { ++ if (evt & ESPI_SYSEVT_HOST_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_HOST_RST_WARN; send ack\n"); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_HOST_RST_ACK, ESPI_SYSEVT_HOST_RST_ACK); ++ } ++ } ++ if (sts & ESPI_SYSEVT_OOB_RST_WARN) { ++ if (evt & ESPI_SYSEVT_OOB_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; send ack\n"); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_OOB_RST_ACK, ESPI_SYSEVT_OOB_RST_ACK); ++ } ++ } ++#endif ++ regmap_write(priv->map, ESPI_SYS_ISR, sts); ++} ++ ++static void aspeed_espi_slave_sys1_event(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 sts, evt; ++ ++ if (regmap_read(priv->map, ESPI_SYS1_ISR, &sts) != 0 || ++ regmap_read(priv->map, ESPI_SYS1_EVENT, &evt) != 0) { ++ dev_err(dev, "regmap_read failed\n"); ++ return; ++ } ++ dev_dbg(dev, "sys1: sts = %08x, evt = %08x\n", sts, evt); ++ ++#if 0 ++ if (sts & ESPI_SYSEVT1_SUS_WARN) { ++ dev_info(dev, "ESPI_SYSEVT1_SUS_WARN; %s ack\n", ++ (evt & ESPI_SYSEVT1_SUS_WARN ? "send" : "clr")); ++ regmap_write_bits(priv->map, ESPI_SYS1_EVENT, ++ ESPI_SYSEVT1_SUS_ACK, ++ evt & ESPI_SYSEVT1_SUS_WARN ? ++ ESPI_SYSEVT1_SUS_ACK : 0); ++ } ++#else ++ if (sts & ESPI_SYSEVT1_SUS_WARN) { ++ if (evt & ESPI_SYSEVT1_SUS_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; send ack\n"); ++ regmap_write_bits(priv->map, ESPI_SYS1_EVENT, ++ ESPI_SYSEVT1_SUS_ACK, ESPI_SYSEVT1_SUS_ACK); ++ } ++ } ++#endif ++ regmap_write(priv->map, ESPI_SYS1_ISR, sts); ++} ++ ++static void aspeed_espi_slave_boot_ack(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 evt; ++ ++ if (regmap_read(priv->map, ESPI_SYS_EVENT, &evt) == 0 && ++ (evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS) == 0) { ++ dev_info(dev, "Setting espi slave boot done\n"); ++ regmap_write(priv->map, ESPI_SYS_EVENT, ++ evt | ESPI_SYSEVT_SLAVE_BOOT_STATUS | ++ ESPI_SYSEVT_SLAVE_BOOT_DONE); ++ } ++ ++ if (regmap_read(priv->map, ESPI_SYS1_EVENT, &evt) == 0 && ++ (evt & ESPI_SYSEVT1_SUS_WARN) != 0 && ++ (evt & ESPI_SYSEVT1_SUS_ACK) == 0) { ++ dev_info(dev, "Boot SUS WARN set; send ack\n"); ++ regmap_write(priv->map, ESPI_SYS1_EVENT, ++ evt | ESPI_SYSEVT1_SUS_ACK); ++ } ++} ++ ++static irqreturn_t aspeed_espi_slave_irq(int irq, void *arg) ++{ ++ struct platform_device *pdev = arg; ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 sts; ++ ++ if (regmap_read(priv->map, ESPI_ISR, &sts) != 0) { ++ dev_err(dev, "regmap_read failed\n"); ++ return IRQ_NONE; ++ } ++ ++ dev_dbg(dev, "ESPI_ISR: %08x\n", sts); ++ ++ if (sts & ESPI_ISR_VW_SYS_EVT) ++ aspeed_espi_slave_sys_event(pdev); ++ ++ if (sts & ESPI_ISR_VW_SYS_EVT1) ++ aspeed_espi_slave_sys1_event(pdev); ++ ++ /* ++ if (sts & ESPI_ISR_HW_RESET) { ++ regmap_write_bits(priv->map, ESPI_CTRL, ++ ESPI_CTRL_SW_RESET, 0); ++ regmap_write_bits(priv->map, ESPI_CTRL, ++ ESPI_CTRL_SW_RESET, ESPI_CTRL_SW_RESET); ++ ++ aspeed_espi_slave_boot_ack(pdev); ++ } ++ */ ++ ++ regmap_write(priv->map, ESPI_ISR, sts); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Setup Interrupt Type/Enable of System Event from Master ++ * T2 T1 T0 ++ * 1). HOST_RST_WARN : Dual Edge 1 0 0 ++ * 2). OOB_RST_WARN : Dual Edge 1 0 0 ++ * 3). PLTRST_N : Dual Edge 1 0 0 ++ */ ++#define ESPI_SYS_INT_T0_SET 0x00000000 ++#define ESPI_SYS_INT_T1_SET 0x00000000 ++#define ESPI_SYS_INT_T2_SET \ ++(ESPI_SYSEVT_HOST_RST_WARN | ESPI_SYSEVT_OOB_RST_WARN | ESPI_SYSEVT_PLT_RST_N) ++#define ESPI_SYS_INT_SET \ ++(ESPI_SYSEVT_HOST_RST_WARN | ESPI_SYSEVT_OOB_RST_WARN | ESPI_SYSEVT_PLT_RST_N) ++ ++/* Setup Interrupt Type/Enable of System Event 1 from Master ++ * T2 T1 T0 ++ * 1). SUS_WARN : Rising Edge 0 0 1 ++ */ ++#define ESPI_SYS1_INT_T0_SET ESPI_SYSEVT1_SUS_WARN ++#define ESPI_SYS1_INT_T1_SET 0x00000000 ++#define ESPI_SYS1_INT_T2_SET 0x00000000 ++#define ESPI_SYS1_INT_SET ESPI_SYSEVT1_SUS_WARN ++ ++static int aspeed_espi_slave_config_irq(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ int irq; ++ int rc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ regmap_write_bits(priv->map, ESPI_CTRL, ESPI_CTRL_OOB_CHRDY, ++ ESPI_CTRL_OOB_CHRDY); ++ ++ regmap_write(priv->map, ESPI_SYS_INT_T0, ESPI_SYS_INT_T0_SET); ++ regmap_write(priv->map, ESPI_SYS_INT_T1, ESPI_SYS_INT_T1_SET); ++ regmap_write(priv->map, ESPI_SYS_INT_T2, ESPI_SYS_INT_T2_SET); ++ regmap_write(priv->map, ESPI_SYS_IER, ESPI_SYS_INT_SET); ++ ++ regmap_write(priv->map, ESPI_SYS1_INT_T0, ESPI_SYS1_INT_T0_SET); ++ regmap_write(priv->map, ESPI_SYS1_INT_T1, ESPI_SYS1_INT_T1_SET); ++ regmap_write(priv->map, ESPI_SYS1_INT_T2, ESPI_SYS1_INT_T2_SET); ++ regmap_write(priv->map, ESPI_SYS1_IER, ESPI_SYS1_INT_SET); ++ ++ regmap_write(priv->map, ESPI_IER, 0xFFFFFFFF); ++ ++ aspeed_espi_slave_boot_ack(pdev); ++ ++ rc = devm_request_irq(dev, irq, aspeed_espi_slave_irq, IRQF_SHARED, ++ dev_name(dev), pdev); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++static const struct regmap_config espi_slave_regmap_cfg = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = ESPI_SYS1_ISR, ++}; ++ ++static int aspeed_espi_slave_probe(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ void __iomem *regs; ++ int rc; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->map = devm_regmap_init_mmio(dev, regs, &espi_slave_regmap_cfg); ++ if (IS_ERR(priv->map)) ++ return PTR_ERR(priv->map); ++ ++ priv->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ dev_err(dev, "couldn't get clock\n"); ++ return PTR_ERR(priv->clk); ++ } ++ rc = clk_prepare_enable(priv->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ ++ dev_set_name(dev, DEVICE_NAME); ++ ++ platform_set_drvdata(pdev, priv); ++ ++ rc = aspeed_espi_slave_config_irq(pdev); ++ if (rc) { ++ platform_set_drvdata(pdev, NULL); ++ goto err; ++ } ++ ++ dev_info(dev, "aspeed,ast2500-espi-slave probe complete\n"); ++ return 0; ++ ++err: ++ clk_disable_unprepare(priv->clk); ++ return rc; ++} ++ ++ ++static int aspeed_espi_slave_remove(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id of_espi_slave_match_table[] = { ++ { .compatible = "aspeed,ast2500-espi-slave" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, of_espi_slave_match_table); ++ ++static struct platform_driver aspeed_espi_slave_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = of_match_ptr(of_espi_slave_match_table), ++ }, ++ .probe = aspeed_espi_slave_probe, ++ .remove = aspeed_espi_slave_remove, ++}; ++module_platform_driver(aspeed_espi_slave_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); ++MODULE_DESCRIPTION("Linux device interface to the eSPI slave"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch new file mode 100644 index 000000000..922a45787 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch @@ -0,0 +1,387 @@ +From 23a7407c3f1bab7c01b93eeced4e137601ac1c94 Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@intel.com> +Date: Wed, 4 Apr 2018 13:52:39 -0700 +Subject: [PATCH] Add support for new PECI commands + +Signed-off-by: Jason M. Bills <jason.m.bills@intel.com> +--- + drivers/peci/peci-core.c | 223 ++++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/peci-ioctl.h | 102 ++++++++++++++++++ + 2 files changed, 325 insertions(+) + +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index 14048a13ef8a..2f7e795158ce 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -344,6 +344,9 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter) + adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP); + adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB); + adapter->cmd_mask |= BIT(PECI_CMD_PING); ++ adapter->cmd_mask |= BIT(PECI_CMD_RD_END_PT_CFG); ++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_DISC); ++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_GET_FRAME); + + out: + peci_put_xfer_msg(msg); +@@ -686,6 +689,223 @@ static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + return ret; + } + ++static int peci_cmd_rd_end_pt_cfg(struct peci_adapter *adapter, void *vmsg) ++{ ++ struct peci_rd_end_pt_cfg_msg *umsg = vmsg; ++ struct peci_xfer_msg *msg = NULL; ++ u32 address; ++ int ret; ++ ++ switch (umsg->msg_type) { ++ case PECI_RDENDPTCFG_TYPE_LOCAL_PCI: ++ case PECI_RDENDPTCFG_TYPE_PCI: ++ /* ++ * Per the PECI spec, the read length must be a byte, word, ++ * or dword ++ */ ++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && ++ umsg->rx_len != 4) { ++ dev_dbg(&adapter->dev, ++ "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ ++ msg = peci_get_xfer_msg(PECI_RDENDPTCFG_PCI_WRITE_LEN, ++ PECI_RDENDPTCFG_READ_LEN_BASE + ++ umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ ++ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */ ++ address |= (u32)umsg->params.pci_cfg.function ++ << 12; /* [14:12] - Function */ ++ address |= (u32)umsg->params.pci_cfg.device ++ << 15; /* [19:15] - Device */ ++ address |= (u32)umsg->params.pci_cfg.bus ++ << 20; /* [27:20] - Bus */ ++ /* [31:28] - Reserved */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD; ++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */ ++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */ ++ msg->tx_buf[3] = 0x00; /* Endpoint ID */ ++ msg->tx_buf[4] = 0x00; /* Reserved */ ++ msg->tx_buf[5] = 0x00; /* Reserved */ ++ msg->tx_buf[6] = PECI_RDENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */ ++ msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */ ++ msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */ ++ msg->tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */ ++ msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */ ++ msg->tx_buf[11] = ++ (u8)(address >> 24); /* MSB - PCI Config Address */ ++ break; ++ ++ case PECI_RDENDPTCFG_TYPE_MMIO: ++ /* ++ * Per the PECI spec, the read length must be a byte, word, ++ * dword, or qword ++ */ ++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && ++ umsg->rx_len != 4 && umsg->rx_len != 8) { ++ dev_dbg(&adapter->dev, ++ "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ /* ++ * Per the PECI spec, the address type must specify either DWORD ++ * or QWORD ++ */ ++ if (umsg->params.mmio.addr_type != ++ PECI_RDENDPTCFG_ADDR_TYPE_MMIO_D && ++ umsg->params.mmio.addr_type != ++ PECI_RDENDPTCFG_ADDR_TYPE_MMIO_Q) { ++ dev_dbg(&adapter->dev, ++ "Invalid address type, addr_type: %d\n", ++ umsg->params.mmio.addr_type); ++ return -EINVAL; ++ } ++ ++ msg = peci_get_xfer_msg(PECI_RDENDPTCFG_MMIO_D_WRITE_LEN, ++ PECI_RDENDPTCFG_READ_LEN_BASE + ++ umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ ++ address = umsg->params.mmio.function; /* [2:0] - Function */ ++ address |= (u32)umsg->params.mmio.device ++ << 3; /* [7:3] - Device */ ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD; ++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */ ++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */ ++ msg->tx_buf[3] = 0x00; /* Endpoint ID */ ++ msg->tx_buf[4] = 0x00; /* Reserved */ ++ msg->tx_buf[5] = umsg->params.mmio.bar; /* BAR # */ ++ msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */ ++ msg->tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */ ++ msg->tx_buf[8] = (u8)address; /* Function/Device */ ++ msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */ ++ msg->tx_buf[10] = (u8)umsg->params.mmio ++ .offset; /* LSB - Register Offset */ ++ msg->tx_buf[11] = (u8)(umsg->params.mmio.offset ++ >> 8); /* Register Offset */ ++ msg->tx_buf[12] = (u8)(umsg->params.mmio.offset ++ >> 16); /* Register Offset */ ++ msg->tx_buf[13] = (u8)(umsg->params.mmio.offset ++ >> 24); /* MSB - DWORD Register Offset */ ++ if (umsg->params.mmio.addr_type == ++ PECI_RDENDPTCFG_ADDR_TYPE_MMIO_Q) { ++ msg->tx_len = PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN; ++ msg->tx_buf[14] = (u8)(umsg->params.mmio.offset ++ >> 32); /* Register Offset */ ++ msg->tx_buf[15] = (u8)(umsg->params.mmio.offset ++ >> 40); /* Register Offset */ ++ msg->tx_buf[16] = (u8)(umsg->params.mmio.offset ++ >> 48); /* Register Offset */ ++ msg->tx_buf[17] = ++ (u8)(umsg->params.mmio.offset ++ >> 56); /* MSB - QWORD Register Offset */ ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; ++} ++ ++static int peci_cmd_crashdump_disc(struct peci_adapter *adapter, void *vmsg) ++{ ++ struct peci_crashdump_disc_msg *umsg = vmsg; ++ struct peci_xfer_msg *msg; ++ int ret; ++ ++ /* Per the EDS, the read length must be a byte, word, or qword */ ++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 8) { ++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ ++ msg = peci_get_xfer_msg(PECI_CRASHDUMP_DISC_WRITE_LEN, ++ PECI_CRASHDUMP_DISC_READ_LEN_BASE + ++ umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_CRASHDUMP_CMD; ++ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = PECI_CRASHDUMP_DISC_VERSION; ++ msg->tx_buf[3] = PECI_CRASHDUMP_DISC_OPCODE; ++ msg->tx_buf[4] = umsg->subopcode; ++ msg->tx_buf[5] = umsg->param0; ++ msg->tx_buf[6] = (u8)umsg->param1; ++ msg->tx_buf[7] = (u8)(umsg->param1 >> 8); ++ msg->tx_buf[8] = umsg->param2; ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; ++} ++ ++static int peci_cmd_crashdump_get_frame(struct peci_adapter *adapter, ++ void *vmsg) ++{ ++ struct peci_crashdump_get_frame_msg *umsg = vmsg; ++ struct peci_xfer_msg *msg; ++ int ret; ++ ++ /* Per the EDS, the read length must be a qword or dqword */ ++ if (umsg->rx_len != 8 && umsg->rx_len != 16) { ++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ ++ msg = peci_get_xfer_msg(PECI_CRASHDUMP_GET_FRAME_WRITE_LEN, ++ PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE + ++ umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_CRASHDUMP_CMD; ++ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = PECI_CRASHDUMP_GET_FRAME_VERSION; ++ msg->tx_buf[3] = PECI_CRASHDUMP_GET_FRAME_OPCODE; ++ msg->tx_buf[4] = (u8)umsg->param0; ++ msg->tx_buf[5] = (u8)(umsg->param0 >> 8); ++ msg->tx_buf[6] = (u8)umsg->param1; ++ msg->tx_buf[7] = (u8)(umsg->param1 >> 8); ++ msg->tx_buf[8] = (u8)umsg->param2; ++ msg->tx_buf[8] = (u8)(umsg->param2 >> 8); ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; ++} ++ + typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *); + + static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = { +@@ -701,6 +921,9 @@ static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = { + peci_cmd_wr_pci_cfg, + peci_cmd_rd_pci_cfg_local, + peci_cmd_wr_pci_cfg_local, ++ peci_cmd_rd_end_pt_cfg, ++ peci_cmd_crashdump_disc, ++ peci_cmd_crashdump_get_frame, + }; + + /** +diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h +index 8467b2fbee1f..090b02c4de49 100644 +--- a/include/uapi/linux/peci-ioctl.h ++++ b/include/uapi/linux/peci-ioctl.h +@@ -70,6 +70,9 @@ enum peci_cmd { + PECI_CMD_WR_PCI_CFG, + PECI_CMD_RD_PCI_CFG_LOCAL, + PECI_CMD_WR_PCI_CFG_LOCAL, ++ PECI_CMD_RD_END_PT_CFG, ++ PECI_CMD_CRASHDUMP_DISC, ++ PECI_CMD_CRASHDUMP_GET_FRAME, + PECI_CMD_MAX + }; + +@@ -420,6 +423,93 @@ struct peci_wr_pci_cfg_local_msg { + __u32 value; + } __attribute__((__packed__)); + ++struct peci_rd_end_pt_cfg_msg { ++#define PECI_RDENDPTCFG_PCI_WRITE_LEN 0x0C ++#define PECI_RDENDPTCFG_MMIO_D_WRITE_LEN 0x0E ++#define PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN 0x12 ++#define PECI_RDENDPTCFG_READ_LEN_BASE 1 ++#define PECI_RDENDPTCFG_CMD 0xC1 ++ ++ __u8 addr; ++ __u8 msg_type; ++#define PECI_RDENDPTCFG_TYPE_LOCAL_PCI 0x03 ++#define PECI_RDENDPTCFG_TYPE_PCI 0x04 ++#define PECI_RDENDPTCFG_TYPE_MMIO 0x05 ++ ++ union { ++ struct { ++ __u8 seg; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ } pci_cfg; ++ struct { ++ __u8 seg; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u8 bar; ++ __u8 addr_type; ++#define PECI_RDENDPTCFG_ADDR_TYPE_PCI 0x04 ++#define PECI_RDENDPTCFG_ADDR_TYPE_MMIO_D 0x05 ++#define PECI_RDENDPTCFG_ADDR_TYPE_MMIO_Q 0x06 ++ ++ __u64 offset; ++ } mmio; ++ } params; ++ __u8 rx_len; ++ __u8 padding[3]; ++ __u8 data[8]; ++} __attribute__((__packed__)); ++ ++/* Crashdump Agent */ ++#define PECI_CRASHDUMP_CORE 0x00 ++#define PECI_CRASHDUMP_TOR 0x01 ++ ++/* Crashdump Agent Param */ ++#define PECI_CRASHDUMP_PAYLOAD_SIZE 0x00 ++ ++/* Crashdump Agent Data Param */ ++#define PECI_CRASHDUMP_AGENT_ID 0x00 ++#define PECI_CRASHDUMP_AGENT_PARAM 0x01 ++ ++struct peci_crashdump_disc_msg { ++ __u8 addr; ++ __u8 subopcode; ++#define PECI_CRASHDUMP_ENABLED 0x00 ++#define PECI_CRASHDUMP_NUM_AGENTS 0x01 ++#define PECI_CRASHDUMP_AGENT_DATA 0x02 ++ ++ __u8 param0; ++ __u8 padding; ++ __u16 param1; ++ __u8 param2; ++ __u8 rx_len; ++ __u8 data[8]; ++} __attribute__((__packed__)); ++ ++struct peci_crashdump_get_frame_msg { ++#define PECI_CRASHDUMP_DISC_WRITE_LEN 9 ++#define PECI_CRASHDUMP_DISC_READ_LEN_BASE 1 ++#define PECI_CRASHDUMP_DISC_VERSION 1 ++#define PECI_CRASHDUMP_DISC_OPCODE 1 ++#define PECI_CRASHDUMP_GET_FRAME_WRITE_LEN 10 ++#define PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE 1 ++#define PECI_CRASHDUMP_GET_FRAME_VERSION 3 ++#define PECI_CRASHDUMP_GET_FRAME_OPCODE 3 ++#define PECI_CRASHDUMP_CMD 0x71 ++ ++ __u8 addr; ++ __u8 padding0; ++ __u16 param0; ++ __u16 param1; ++ __u16 param2; ++ __u8 rx_len; ++ __u8 padding1[3]; ++ __u8 data[16]; ++} __attribute__((__packed__)); ++ + #define PECI_IOC_BASE 0xb7 + + #define PECI_IOC_XFER \ +@@ -460,4 +550,16 @@ struct peci_wr_pci_cfg_local_msg { + _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \ + struct peci_wr_pci_cfg_local_msg) + ++#define PECI_IOC_RD_END_PT_CFG \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_END_PT_CFG, \ ++ struct peci_rd_end_pt_cfg_msg) ++ ++#define PECI_IOC_CRASHDUMP_DISC \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \ ++ struct peci_crashdump_disc_msg) ++ ++#define PECI_IOC_CRASHDUMP_GET_FRAME \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \ ++ struct peci_crashdump_get_frame_msg) ++ + #endif /* __PECI_IOCTL_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch new file mode 100644 index 000000000..860a1ba5d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch @@ -0,0 +1,1138 @@ +From 43470f186979483ba6c1e6374c7ea3a129622862 Mon Sep 17 00:00:00 2001 +From: "Hunt, Bryan" <bryan.hunt@intel.com> +Date: Fri, 30 Mar 2018 10:48:01 -0700 +Subject: [PATCH] Add AST2500d JTAG driver + +Adding aspeed jtag driver + +Signed-off-by: Hunt, Bryan <bryan.hunt@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 9 + + drivers/Kconfig | 1 + + drivers/Makefile | 1 + + drivers/jtag/Kconfig | 13 + + drivers/jtag/Makefile | 1 + + drivers/jtag/jtag_aspeed.c | 963 +++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/jtag_drv.h | 73 +++ + 7 files changed, 1061 insertions(+) + create mode 100644 drivers/jtag/Kconfig + create mode 100644 drivers/jtag/Makefile + create mode 100644 drivers/jtag/jtag_aspeed.c + create mode 100644 include/uapi/linux/jtag_drv.h + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index a7bbc2adecc9..b63003c2c0c7 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -366,6 +366,15 @@ + pinctrl-0 = <&pinctrl_espi_default>; + }; + ++ jtag: jtag@1e6e4000 { ++ compatible = "aspeed,ast2500-jtag"; ++ reg = <0x1e6e2004 0x4 0x1e6e4000 0x1c>; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>; ++ interrupts = <43>; ++ status = "disabled"; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2500-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +diff --git a/drivers/Kconfig b/drivers/Kconfig +index c633db2b41fb..2778a5c33ca5 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -221,4 +221,5 @@ source "drivers/slimbus/Kconfig" + + source "drivers/peci/Kconfig" + ++source "drivers/jtag/Kconfig" + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 63c9b425e6e1..714067945fd2 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -187,3 +187,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ + obj-$(CONFIG_SIOX) += siox/ + obj-$(CONFIG_GNSS) += gnss/ + obj-$(CONFIG_PECI) += peci/ ++obj-$(CONFIG_JTAG_ASPEED) += jtag/ +diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig +new file mode 100644 +index 000000000000..2e5d0a5bea90 +--- /dev/null ++++ b/drivers/jtag/Kconfig +@@ -0,0 +1,13 @@ ++menuconfig JTAG_ASPEED ++ tristate "ASPEED SoC JTAG controller support" ++ depends on HAS_IOMEM ++ depends on ARCH_ASPEED || COMPILE_TEST ++ help ++ This provides a support for ASPEED JTAG device, equipped on ++ ASPEED SoC 24xx and 25xx families. Drivers allows programming ++ of hardware devices, connected to SoC through the JTAG interface. ++ ++ If you want this support, you should say Y here. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called jtag_aspeed. +diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile +new file mode 100644 +index 000000000000..db9b660e9f90 +--- /dev/null ++++ b/drivers/jtag/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_JTAG_ASPEED) += jtag_aspeed.o +diff --git a/drivers/jtag/jtag_aspeed.c b/drivers/jtag/jtag_aspeed.c +new file mode 100644 +index 000000000000..42e2a131873c +--- /dev/null ++++ b/drivers/jtag/jtag_aspeed.c +@@ -0,0 +1,963 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2018 Intel Corporation ++ ++#include <linux/bitfield.h> ++#include <linux/delay.h> ++#include <linux/fs.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/jtag_drv.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#define SCU_RESET_JTAG BIT(22) ++ ++#define AST_JTAG_DATA 0x00 ++#define AST_JTAG_INST 0x04 ++#define AST_JTAG_CTRL 0x08 ++#define AST_JTAG_ISR 0x0C ++#define AST_JTAG_SW 0x10 ++#define AST_JTAG_TCK 0x14 ++#define AST_JTAG_IDLE 0x18 ++ ++/* AST_JTAG_CTRL - 0x08 : Engine Control */ ++#define JTAG_ENG_EN BIT(31) ++#define JTAG_ENG_OUT_EN BIT(30) ++#define JTAG_ENGINE_EN (JTAG_ENG_EN | JTAG_ENG_OUT_EN) ++#define JTAG_FORCE_TMS BIT(29) ++ ++#define JTAG_IR_UPDATE BIT(26) /* AST2500 only */ ++#define JTAG_INST_LEN_MASK GENMASK(25, 20) ++#define JTAG_LAST_INST BIT(17) ++#define JTAG_INST_EN BIT(16) ++#define JTAG_DATA_LEN_MASK GENMASK(9, 4) ++ ++#define JTAG_DR_UPDATE BIT(10) /* AST2500 only */ ++#define JTAG_LAST_DATA BIT(1) ++#define JTAG_DATA_EN BIT(0) ++ ++/* AST_JTAG_ISR - 0x0C : Interrupt status and enable */ ++#define JTAG_INST_PAUSE BIT(19) ++#define JTAG_INST_COMPLETE BIT(18) ++#define JTAG_DATA_PAUSE BIT(17) ++#define JTAG_DATA_COMPLETE BIT(16) ++ ++#define JTAG_INST_PAUSE_EN BIT(3) ++#define JTAG_INST_COMPLETE_EN BIT(2) ++#define JTAG_DATA_PAUSE_EN BIT(1) ++#define JTAG_DATA_COMPLETE_EN BIT(0) ++ ++/* AST_JTAG_SW - 0x10 : Software Mode and Status */ ++#define JTAG_SW_MODE_EN BIT(19) ++#define JTAG_SW_MODE_TCK BIT(18) ++#define JTAG_SW_MODE_TMS BIT(17) ++#define JTAG_SW_MODE_TDIO BIT(16) ++ ++/* AST_JTAG_TCK - 0x14 : TCK Control */ ++#define JTAG_TCK_DIVISOR_MASK GENMASK(10, 0) ++ ++/* #define USE_INTERRUPTS */ ++#define AST_JTAG_NAME "jtag" ++ ++static DEFINE_SPINLOCK(jtag_state_lock); ++ ++struct ast_jtag_info { ++ void __iomem *reg_base; ++ void __iomem *reg_base_scu; ++ int irq; ++ u32 flag; ++ wait_queue_head_t jtag_wq; ++ bool is_open; ++ struct device *dev; ++ struct miscdevice miscdev; ++}; ++ ++/* ++ * This structure represents a TMS cycle, as expressed in a set of bits and a ++ * count of bits (note: there are no start->end state transitions that require ++ * more than 1 byte of TMS cycles) ++ */ ++struct tms_cycle { ++ unsigned char tmsbits; ++ unsigned char count; ++}; ++ ++/* ++ * These are the string representations of the TAP states corresponding to the ++ * enums literals in JtagStateEncode ++ */ ++static const char * const c_statestr[] = {"TLR", "RTI", "SelDR", "CapDR", ++ "ShfDR", "Ex1DR", "PauDR", "Ex2DR", ++ "UpdDR", "SelIR", "CapIR", "ShfIR", ++ "Ex1IR", "PauIR", "Ex2IR", "UpdIR"}; ++ ++/* ++ * This is the complete set TMS cycles for going from any TAP state to any ++ * other TAP state, following a “shortest path” rule. ++ */ ++static const struct tms_cycle _tms_cycle_lookup[][16] = { ++/* TLR RTI SelDR CapDR SDR Ex1DR PDR Ex2DR UpdDR SelIR CapIR SIR Ex1IR PIR Ex2IR UpdIR*/ ++/* TLR */{ {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, {0x0a, 4}, {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, {0x06, 4}, {0x06, 5}, {0x16, 5}, {0x16, 6}, {0x56, 7}, {0x36, 6} }, ++/* RTI */{ {0x07, 3}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, ++/* SelDR*/{ {0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4} }, ++/* CapDR*/{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++/* SDR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++/* Ex1DR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, ++/* PDR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++/* Ex2DR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, ++/* UpdDR*/{ {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, ++/* SelIR*/{ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, {0x15, 5}, {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3} }, ++/* CapIR*/{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, ++/* SIR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, ++/* Ex1IR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1} }, ++/* PIR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x01, 2}, {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2} }, ++/* Ex2IR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1} }, ++/* UpdIR*/{ {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x00, 0} }, ++}; ++ ++static const char * const regnames[] = { ++ [AST_JTAG_DATA] = "AST_JTAG_DATA", ++ [AST_JTAG_INST] = "AST_JTAG_INST", ++ [AST_JTAG_CTRL] = "AST_JTAG_CTRL", ++ [AST_JTAG_ISR] = "AST_JTAG_ISR", ++ [AST_JTAG_SW] = "AST_JTAG_SW", ++ [AST_JTAG_TCK] = "AST_JTAG_TCK", ++ [AST_JTAG_IDLE] = "AST_JTAG_IDLE", ++}; ++ ++static inline u32 ast_jtag_read(struct ast_jtag_info *ast_jtag, u32 reg) ++{ ++ u32 val = readl(ast_jtag->reg_base + reg); ++ ++ dev_dbg(ast_jtag->dev, "read:%s val = 0x%08x\n", regnames[reg], val); ++ return val; ++} ++ ++static inline void ast_jtag_write(struct ast_jtag_info *ast_jtag, u32 val, ++ u32 reg) ++{ ++ dev_dbg(ast_jtag->dev, "write:%s val = 0x%08x\n", regnames[reg], val); ++ writel(val, ast_jtag->reg_base + reg); ++} ++ ++static void ast_jtag_set_tck(struct ast_jtag_info *ast_jtag, ++ enum xfer_mode mode, uint tck) ++{ ++ u32 read_value; ++ ++ if (tck == 0) ++ tck = 1; ++ else if (tck > JTAG_TCK_DIVISOR_MASK) ++ tck = JTAG_TCK_DIVISOR_MASK; ++ read_value = ast_jtag_read(ast_jtag, AST_JTAG_TCK); ++ ast_jtag_write(ast_jtag, ++ ((read_value & ~JTAG_TCK_DIVISOR_MASK) | tck), ++ AST_JTAG_TCK); ++} ++ ++static void ast_jtag_get_tck(struct ast_jtag_info *ast_jtag, ++ enum xfer_mode mode, uint *tck) ++{ ++ *tck = FIELD_GET(JTAG_TCK_DIVISOR_MASK, ++ ast_jtag_read(ast_jtag, AST_JTAG_TCK)); ++} ++ ++/* ++ * Used only in SW mode to walk the JTAG state machine. ++ */ ++static u8 tck_cycle(struct ast_jtag_info *ast_jtag, u8 TMS, u8 TDI, ++ bool do_read) ++{ ++ u8 result = 0; ++ u32 regwriteval = JTAG_SW_MODE_EN | (TMS * JTAG_SW_MODE_TMS) ++ | (TDI * JTAG_SW_MODE_TDIO); ++ ++ /* TCK = 0 */ ++ ast_jtag_write(ast_jtag, regwriteval, AST_JTAG_SW); ++ ++ ast_jtag_read(ast_jtag, AST_JTAG_SW); ++ ++ /* TCK = 1 */ ++ ast_jtag_write(ast_jtag, JTAG_SW_MODE_TCK | regwriteval, AST_JTAG_SW); ++ ++ if (do_read) { ++ result = (ast_jtag_read(ast_jtag, AST_JTAG_SW) ++ & JTAG_SW_MODE_TDIO) ? 1 : 0; ++ } ++ return result; ++} ++ ++#define WAIT_ITERATIONS 75 ++ ++static int ast_jtag_wait_instr_pause_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_INST_PAUSE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_INST_PAUSE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for instruction pause complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, JTAG_INST_PAUSE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ast_jtag_wait_instr_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_INST_COMPLETE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_INST_COMPLETE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for instruction complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, JTAG_INST_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ast_jtag_wait_data_pause_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_DATA_PAUSE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_DATA_PAUSE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for data pause complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, JTAG_DATA_PAUSE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ast_jtag_wait_data_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_DATA_COMPLETE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_DATA_COMPLETE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for data complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, ++ JTAG_DATA_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static void ast_jtag_bitbang(struct ast_jtag_info *ast_jtag, ++ struct tck_bitbang *bit_bang) ++{ ++ bit_bang->tdo = tck_cycle(ast_jtag, bit_bang->tms, bit_bang->tdi, true); ++} ++ ++static void reset_tap(struct ast_jtag_info *ast_jtag, enum xfer_mode mode) ++{ ++ unsigned char i; ++ ++ if (mode == SW_MODE) { ++ for (i = 0; i < 9; i++) ++ tck_cycle(ast_jtag, 1, 0, false); ++ } else { ++ ast_jtag_write(ast_jtag, 0, AST_JTAG_SW); ++ mdelay(1); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_FORCE_TMS, ++ AST_JTAG_CTRL); ++ mdelay(1); ++ ast_jtag_write(ast_jtag, ++ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ } ++} ++ ++static int ast_jtag_set_tapstate(struct ast_jtag_info *ast_jtag, ++ enum xfer_mode mode, uint from, uint to) ++{ ++ unsigned char num_cycles; ++ unsigned char cycle; ++ unsigned char tms_bits; ++ ++ /* ++ * Ensure that the requested and current tap states are within ++ * 0 to 15. ++ */ ++ if (from >= ARRAY_SIZE(_tms_cycle_lookup[0]) || /* Column */ ++ to >= ARRAY_SIZE(_tms_cycle_lookup)) { /* row */ ++ return -1; ++ } ++ ++ dev_dbg(ast_jtag->dev, "Set TAP state: %s\n", c_statestr[to]); ++ ++ if (mode == SW_MODE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ ++ if (to == jtag_tlr) { ++ reset_tap(ast_jtag, mode); ++ } else { ++ tms_bits = _tms_cycle_lookup[from][to].tmsbits; ++ num_cycles = _tms_cycle_lookup[from][to].count; ++ ++ if (num_cycles == 0) ++ return 0; ++ ++ for (cycle = 0; cycle < num_cycles; cycle++) { ++ tck_cycle(ast_jtag, (tms_bits & 1), 0, false); ++ tms_bits >>= 1; ++ } ++ } ++ } else if (to == jtag_tlr) { ++ reset_tap(ast_jtag, mode); ++ } ++ return 0; ++} ++ ++static void software_readwrite_scan(struct ast_jtag_info *ast_jtag, ++ struct scan_xfer *scan_xfer) ++{ ++ uint bit_index = 0; ++ bool is_IR = (scan_xfer->tap_state == jtag_shf_ir); ++ uint exit_tap_state = is_IR ? jtag_ex1_ir : jtag_ex1_dr; ++ unsigned char *tdi = scan_xfer->tdi; ++ unsigned char *tdo = scan_xfer->tdo; ++ ++ dev_dbg(ast_jtag->dev, "SW JTAG SHIFT %s, length = %d\n", ++ is_IR ? "IR" : "DR", scan_xfer->length); ++ ++ ast_jtag_write(ast_jtag, ++ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ ++ while (bit_index < scan_xfer->length) { ++ int bit_offset = (bit_index % 8); ++ int this_input_bit = 0; ++ int tms_high_or_low; ++ int this_output_bit; ++ ++ if (bit_index / 8 < scan_xfer->tdi_bytes) { ++ /* ++ * If we are on a byte boundary, increment the byte ++ * pointers. Don't increment on 0, pointer is already ++ * on the first byte. ++ */ ++ if (bit_index % 8 == 0 && bit_index != 0) ++ tdi++; ++ this_input_bit = (*tdi >> bit_offset) & 1; ++ } ++ /* If this is the last bit, leave TMS high */ ++ tms_high_or_low = (bit_index == scan_xfer->length - 1) && ++ (scan_xfer->end_tap_state != jtag_shf_dr) && ++ (scan_xfer->end_tap_state != jtag_shf_ir); ++ this_output_bit = tck_cycle(ast_jtag, tms_high_or_low, ++ this_input_bit, !!tdo); ++ /* ++ * If it was the last bit in the scan and the end_tap_state is ++ * something other than shiftDR or shiftIR then go to Exit1. ++ * IMPORTANT Note: if the end_tap_state is ShiftIR/DR and ++ * the next call to this function is a shiftDR/IR then the ++ * driver will not change state! ++ */ ++ if (tms_high_or_low) ++ scan_xfer->tap_state = exit_tap_state; ++ if (tdo && bit_index / 8 < scan_xfer->tdo_bytes) { ++ if (bit_index % 8 == 0) { ++ if (bit_index != 0) ++ tdo++; ++ *tdo = 0; ++ } ++ *tdo |= this_output_bit << bit_offset; ++ } ++ bit_index++; ++ } ++ ast_jtag_set_tapstate(ast_jtag, scan_xfer->mode, scan_xfer->tap_state, ++ scan_xfer->end_tap_state); ++} ++ ++static int fire_ir_command(struct ast_jtag_info *ast_jtag, bool last, ++ u32 length) ++{ ++ int res; ++ ++ if (last) { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_INST ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_INST ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length) ++ | JTAG_INST_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_instr_complete(ast_jtag); ++ } else { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_IR_UPDATE ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_IR_UPDATE ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length) ++ | JTAG_INST_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_instr_pause_complete(ast_jtag); ++ } ++ return res; ++} ++ ++static int fire_dr_command(struct ast_jtag_info *ast_jtag, bool last, ++ u32 length) ++{ ++ int res; ++ ++ if (last) { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_DATA ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_DATA ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length) ++ | JTAG_DATA_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_data_complete(ast_jtag); ++ } else { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_DR_UPDATE ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_DR_UPDATE ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length) ++ | JTAG_DATA_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_data_pause_complete(ast_jtag); ++ } ++ return res; ++} ++ ++static int hardware_readwrite_scan(struct ast_jtag_info *ast_jtag, ++ struct scan_xfer *scan_xfer) ++{ ++ int res = 0; ++ u32 bits_received = 0; ++ u32 bits_to_send = 0; ++ u32 chunk_len = 0; ++ bool is_IR = (scan_xfer->tap_state == jtag_shf_ir); ++ bool is_last = false; ++ u32 length = scan_xfer->length; ++ u32 *tdi = (u32 *)scan_xfer->tdi; ++ u32 *tdo = (u32 *)scan_xfer->tdo; ++ u32 remaining_bytes; ++ int scan_end = 0; ++ u32 ast_reg = is_IR ? AST_JTAG_INST : AST_JTAG_DATA; ++ ++ dev_dbg(ast_jtag->dev, "HW JTAG SHIFT %s, length = %d\n", ++ is_IR ? "IR" : "DR", length); ++ ++ ast_jtag_write(ast_jtag, 0, AST_JTAG_SW); ++ if (scan_xfer->end_tap_state == jtag_pau_dr || ++ scan_xfer->end_tap_state == jtag_pau_ir || ++ scan_xfer->end_tap_state == jtag_shf_dr || ++ scan_xfer->end_tap_state == jtag_shf_ir) { ++ scan_end = 0; ++ } else { ++ scan_end = 1; ++ } ++ ++ while (length > 0) { ++ chunk_len = (length > 32) ? 32 : length; ++ ++ if (length <= 32 && scan_end == 1) ++ is_last = true; ++ ++ dev_dbg(ast_jtag->dev, "HW SHIFT, length=%d, scan_end=%d, chunk_len=%d, is_last=%d\n", ++ length, scan_end, chunk_len, is_last); ++ ++ remaining_bytes = (scan_xfer->length - length) / 8; ++ if (tdi && remaining_bytes < scan_xfer->tdi_bytes) { ++ bits_to_send = *tdi++; ++ ast_jtag_write(ast_jtag, bits_to_send, ast_reg); ++ } else { ++ bits_to_send = 0; ++ ast_jtag_write(ast_jtag, 0, ast_reg); ++ } ++ ++ dev_dbg(ast_jtag->dev, "HW SHIFT, len=%d chunk_len=%d is_last=%x bits_to_send=%x\n", ++ length, chunk_len, is_last, bits_to_send); ++ ++ if (is_IR) ++ res = fire_ir_command(ast_jtag, is_last, chunk_len); ++ else ++ res = fire_dr_command(ast_jtag, is_last, chunk_len); ++ if (res != 0) ++ break; ++ ++ if (tdo) { ++ bits_received = ast_jtag_read(ast_jtag, ast_reg); ++ bits_received >>= (32 - chunk_len); ++ *tdo++ = bits_received; ++ } ++ dev_dbg(ast_jtag->dev, ++ "HW SHIFT, len=%d chunk_len=%d is_last=%x bits_received=%x\n", ++ length, chunk_len, is_last, ++ bits_received); ++ length -= chunk_len; ++ } ++ return res; ++} ++ ++static int ast_jtag_readwrite_scan(struct ast_jtag_info *ast_jtag, ++ struct scan_xfer *scan_xfer) ++{ ++ int res = 0; ++ ++ if (scan_xfer->tap_state != jtag_shf_dr && ++ scan_xfer->tap_state != jtag_shf_ir) { ++ if (scan_xfer->tap_state < ARRAY_SIZE(c_statestr)) ++ dev_err(ast_jtag->dev, ++ "readwrite_scan bad current tap state = %s\n", ++ c_statestr[scan_xfer->tap_state]); ++ else ++ dev_err(ast_jtag->dev, ++ "readwrite_scan bad current tap state = %u\n", ++ scan_xfer->tap_state); ++ return -EFAULT; ++ } ++ ++ if (scan_xfer->length == 0) { ++ dev_err(ast_jtag->dev, "readwrite_scan bad length 0\n"); ++ return -EFAULT; ++ } ++ ++ if (!scan_xfer->tdi && scan_xfer->tdi_bytes != 0) { ++ dev_err(ast_jtag->dev, ++ "readwrite_scan null tdi with non-zero length %u!\n", ++ scan_xfer->tdi_bytes); ++ return -EFAULT; ++ } ++ ++ if (!scan_xfer->tdo && scan_xfer->tdo_bytes != 0) { ++ dev_err(ast_jtag->dev, ++ "readwrite_scan null tdo with non-zero length %u!\n", ++ scan_xfer->tdo_bytes); ++ return -EFAULT; ++ } ++ ++ if (!scan_xfer->tdi && !scan_xfer->tdo) { ++ dev_err(ast_jtag->dev, "readwrite_scan null tdo and tdi!\n"); ++ return -EFAULT; ++ } ++ ++ if (scan_xfer->mode == SW_MODE) ++ software_readwrite_scan(ast_jtag, scan_xfer); ++ else ++ res = hardware_readwrite_scan(ast_jtag, scan_xfer); ++ return res; ++} ++ ++#ifdef USE_INTERRUPTS ++static irqreturn_t ast_jtag_interrupt(int this_irq, void *dev_id) ++{ ++ u32 status; ++ struct ast_jtag_info *ast_jtag = dev_id; ++ ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ ++ if (status & JTAG_INST_PAUSE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_INST_PAUSE | (status & 0xf), ++ AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_INST_PAUSE; ++ } ++ ++ if (status & JTAG_INST_COMPLETE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_INST_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_INST_COMPLETE; ++ } ++ ++ if (status & JTAG_DATA_PAUSE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_DATA_PAUSE | (status & 0xf), AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_DATA_PAUSE; ++ } ++ ++ if (status & JTAG_DATA_COMPLETE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_DATA_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_DATA_COMPLETE; ++ } ++ ++ if (ast_jtag->flag) { ++ wake_up_interruptible(&ast_jtag->jtag_wq); ++ return IRQ_HANDLED; ++ } else { ++ return IRQ_NONE; ++ } ++} ++#endif ++ ++static inline void ast_jtag_slave(struct ast_jtag_info *ast_jtag) ++{ ++ u32 currReg = readl((void *)(ast_jtag->reg_base_scu)); ++ ++ writel(currReg | SCU_RESET_JTAG, (void *)ast_jtag->reg_base_scu); ++} ++ ++static inline void ast_jtag_master(struct ast_jtag_info *ast_jtag) ++{ ++ u32 currReg = readl((void *)(ast_jtag->reg_base_scu)); ++ ++ writel(currReg & ~SCU_RESET_JTAG, (void *)ast_jtag->reg_base_scu); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN, AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ ast_jtag_write(ast_jtag, JTAG_INST_PAUSE | JTAG_INST_COMPLETE | ++ JTAG_DATA_PAUSE | JTAG_DATA_COMPLETE | ++ JTAG_INST_PAUSE_EN | JTAG_INST_COMPLETE_EN | ++ JTAG_DATA_PAUSE_EN | JTAG_DATA_COMPLETE_EN, ++ AST_JTAG_ISR); /* Enable Interrupt */ ++} ++ ++static long jtag_ioctl(struct file *file, uint cmd, ulong arg) ++{ ++ int ret = 0; ++ struct ast_jtag_info *ast_jtag = file->private_data; ++ void __user *argp = (void __user *)arg; ++ struct tck_bitbang bitbang; ++ struct scan_xfer xfer; ++ struct set_tck_param set_tck_param; ++ struct get_tck_param get_tck_param; ++ struct tap_state_param tap_state_param; ++ unsigned char *kern_tdi = NULL; ++ unsigned char *kern_tdo = NULL; ++ unsigned char *user_tdi; ++ unsigned char *user_tdo; ++ ++ switch (cmd) { ++ case AST_JTAG_SET_TCK: ++ if (copy_from_user(&set_tck_param, argp, ++ sizeof(struct set_tck_param))) { ++ ret = -EFAULT; ++ } else { ++ ast_jtag_set_tck(ast_jtag, ++ set_tck_param.mode, ++ set_tck_param.tck); ++ } ++ break; ++ case AST_JTAG_GET_TCK: ++ if (copy_from_user(&get_tck_param, argp, ++ sizeof(struct get_tck_param))) ++ ret = -EFAULT; ++ else ++ ast_jtag_get_tck(ast_jtag, ++ get_tck_param.mode, ++ &get_tck_param.tck); ++ if (copy_to_user(argp, &get_tck_param, ++ sizeof(struct get_tck_param))) ++ ret = -EFAULT; ++ break; ++ case AST_JTAG_BITBANG: ++ if (copy_from_user(&bitbang, argp, ++ sizeof(struct tck_bitbang))) { ++ ret = -EFAULT; ++ } else { ++ if (bitbang.tms > 1 || bitbang.tdi > 1) ++ ret = -EFAULT; ++ else ++ ast_jtag_bitbang(ast_jtag, &bitbang); ++ } ++ if (copy_to_user(argp, &bitbang, sizeof(struct tck_bitbang))) ++ ret = -EFAULT; ++ break; ++ case AST_JTAG_SET_TAPSTATE: ++ if (copy_from_user(&tap_state_param, argp, ++ sizeof(struct tap_state_param))) ++ ret = -EFAULT; ++ else ++ ast_jtag_set_tapstate(ast_jtag, tap_state_param.mode, ++ tap_state_param.from_state, ++ tap_state_param.to_state); ++ break; ++ case AST_JTAG_READWRITESCAN: ++ if (copy_from_user(&xfer, argp, ++ sizeof(struct scan_xfer))) { ++ ret = -EFAULT; ++ } else { ++ if (xfer.tdi) { ++ user_tdi = xfer.tdi; ++ kern_tdi = memdup_user(user_tdi, ++ xfer.tdi_bytes); ++ if (IS_ERR(kern_tdi)) ++ ret = -EFAULT; ++ else ++ xfer.tdi = kern_tdi; ++ } ++ ++ if (ret == 0 && xfer.tdo) { ++ user_tdo = xfer.tdo; ++ kern_tdo = memdup_user(user_tdo, ++ xfer.tdo_bytes); ++ if (IS_ERR(kern_tdo)) ++ ret = -EFAULT; ++ else ++ xfer.tdo = kern_tdo; ++ } ++ ++ if (ret == 0) ++ ret = ast_jtag_readwrite_scan(ast_jtag, &xfer); ++ ++ kfree(kern_tdi); ++ if (kern_tdo) { ++ if (ret == 0) { ++ if (copy_to_user(user_tdo, ++ xfer.tdo, ++ xfer.tdo_bytes)) ++ ret = -EFAULT; ++ } ++ kfree(kern_tdo); ++ } ++ } ++ break; ++ default: ++ return -ENOTTY; ++ } ++ ++ return ret; ++} ++ ++static int jtag_open(struct inode *inode, struct file *file) ++{ ++ struct ast_jtag_info *ast_jtag = container_of(file->private_data, ++ struct ast_jtag_info, ++ miscdev); ++ ++ spin_lock(&jtag_state_lock); ++ if (ast_jtag->is_open) { ++ spin_unlock(&jtag_state_lock); ++ return -EBUSY; ++ } ++ ++ ast_jtag->is_open = true; ++ file->private_data = ast_jtag; ++ ast_jtag_master(ast_jtag); ++ spin_unlock(&jtag_state_lock); ++ ++ return 0; ++} ++ ++static int jtag_release(struct inode *inode, struct file *file) ++{ ++ struct ast_jtag_info *ast_jtag = file->private_data; ++ ++ spin_lock(&jtag_state_lock); ++ ast_jtag_slave(ast_jtag); ++ ast_jtag->is_open = false; ++ ++ spin_unlock(&jtag_state_lock); ++ ++ return 0; ++} ++ ++static const struct file_operations ast_jtag_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = jtag_ioctl, ++ .open = jtag_open, ++ .release = jtag_release, ++}; ++ ++static int ast_jtag_probe(struct platform_device *pdev) ++{ ++ struct resource *scu_res; ++ struct resource *jtag_res; ++ int ret = 0; ++ struct ast_jtag_info *ast_jtag; ++ ++ dev_dbg(&pdev->dev, "%s started\n", __func__); ++ ++ scu_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!scu_res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM for SCU\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ jtag_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!jtag_res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM for JTAG\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ ast_jtag = devm_kzalloc(&pdev->dev, sizeof(*ast_jtag), GFP_KERNEL); ++ if (!ast_jtag) ++ return -ENOMEM; ++ ++ ast_jtag->reg_base_scu = devm_ioremap_resource(&pdev->dev, scu_res); ++ if (!ast_jtag->reg_base_scu) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ ast_jtag->reg_base = devm_ioremap_resource(&pdev->dev, jtag_res); ++ if (!ast_jtag->reg_base) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ ast_jtag->dev = &pdev->dev; ++ ++#ifdef USE_INTERRUPTS ++ ast_jtag->irq = platform_get_irq(pdev, 0); ++ if (ast_jtag->irq < 0) { ++ dev_err(&pdev->dev, "no irq specified.\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, ast_jtag->irq, ast_jtag_interrupt, ++ IRQF_SHARED, "ast-jtag", ast_jtag); ++ if (ret) { ++ dev_err(ast_jtag->dev, "JTAG Unable to get IRQ.\n"); ++ goto out; ++ } ++#endif ++ ++ ast_jtag->flag = 0; ++ init_waitqueue_head(&ast_jtag->jtag_wq); ++ ++ ast_jtag->miscdev.minor = MISC_DYNAMIC_MINOR, ++ ast_jtag->miscdev.name = AST_JTAG_NAME, ++ ast_jtag->miscdev.fops = &ast_jtag_fops, ++ ast_jtag->miscdev.parent = &pdev->dev; ++ ret = misc_register(&ast_jtag->miscdev); ++ if (ret) { ++ dev_err(ast_jtag->dev, "Unable to register misc device.\n"); ++ goto out; ++ } ++ ++ platform_set_drvdata(pdev, ast_jtag); ++ ++ ast_jtag_slave(ast_jtag); ++ ++ dev_dbg(&pdev->dev, "%s completed\n", __func__); ++ return 0; ++ ++out: ++ dev_warn(&pdev->dev, "ast_jtag: driver init failed (ret=%d).\n", ret); ++ return ret; ++} ++ ++static int ast_jtag_remove(struct platform_device *pdev) ++{ ++ dev_dbg(&pdev->dev, "%s\n", __func__); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ dev_dbg(&pdev->dev, "JTAG driver removed successfully.\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id ast_jtag_of_match[] = { ++ { .compatible = "aspeed,ast2400-jtag", }, ++ { .compatible = "aspeed,ast2500-jtag", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ast_jtag_of_match); ++ ++static struct platform_driver ast_jtag_driver = { ++ .probe = ast_jtag_probe, ++ .remove = ast_jtag_remove, ++ .driver = { ++ .name = AST_JTAG_NAME, ++ .of_match_table = of_match_ptr(ast_jtag_of_match), ++ }, ++}; ++module_platform_driver(ast_jtag_driver); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Bryan Hunt <bryan.hunt@intel.com>"); ++MODULE_DESCRIPTION("ASPEED JTAG driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/uapi/linux/jtag_drv.h b/include/uapi/linux/jtag_drv.h +new file mode 100644 +index 000000000000..4df638f8fa43 +--- /dev/null ++++ b/include/uapi/linux/jtag_drv.h +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2012-2017 ASPEED Technology Inc. */ ++/* Copyright (c) 2018 Intel Corporation */ ++ ++#ifndef __JTAG_DRV_H__ ++#define __JTAG_DRV_H__ ++ ++enum xfer_mode { ++ HW_MODE = 0, ++ SW_MODE ++} xfer_mode; ++ ++struct tck_bitbang { ++ __u8 tms; ++ __u8 tdi; ++ __u8 tdo; ++} __attribute__((__packed__)); ++ ++struct scan_xfer { ++ __u8 mode; ++ __u32 tap_state; ++ __u32 length; ++ __u8 *tdi; ++ __u32 tdi_bytes; ++ __u8 *tdo; ++ __u32 tdo_bytes; ++ __u32 end_tap_state; ++} __attribute__((__packed__)); ++ ++struct set_tck_param { ++ __u8 mode; ++ __u32 tck; ++} __attribute__((__packed__)); ++ ++struct get_tck_param { ++ __u8 mode; ++ __u32 tck; ++} __attribute__((__packed__)); ++ ++struct tap_state_param { ++ __u8 mode; ++ __u32 from_state; ++ __u32 to_state; ++} __attribute__((__packed__)); ++ ++enum jtag_states { ++ jtag_tlr, ++ jtag_rti, ++ jtag_sel_dr, ++ jtag_cap_dr, ++ jtag_shf_dr, ++ jtag_ex1_dr, ++ jtag_pau_dr, ++ jtag_ex2_dr, ++ jtag_upd_dr, ++ jtag_sel_ir, ++ jtag_cap_ir, ++ jtag_shf_ir, ++ jtag_ex1_ir, ++ jtag_pau_ir, ++ jtag_ex2_ir, ++ jtag_upd_ir ++} jtag_states; ++ ++#define JTAGIOC_BASE 'T' ++ ++#define AST_JTAG_SET_TCK _IOW(JTAGIOC_BASE, 3, struct set_tck_param) ++#define AST_JTAG_GET_TCK _IOR(JTAGIOC_BASE, 4, struct get_tck_param) ++#define AST_JTAG_BITBANG _IOWR(JTAGIOC_BASE, 5, struct tck_bitbang) ++#define AST_JTAG_SET_TAPSTATE _IOW(JTAGIOC_BASE, 6, struct tap_state_param) ++#define AST_JTAG_READWRITESCAN _IOWR(JTAGIOC_BASE, 7, struct scan_xfer) ++ ++#endif +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch new file mode 100644 index 000000000..e2dee0d5b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch @@ -0,0 +1,291 @@ +From a7ad8d09cdf0ec86612df0714d3e69ee92e6140b Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 20 Nov 2018 09:30:17 -0800 +Subject: [PATCH] i2c: aspeed: Improve driver to support multi-master use cases + stably + +In multi-master environment, this driver's master cannot know +exactly when peer master sends data to this driver's slave so +cases can be happened that this master tries to send data through +the master_xfer function but slave data from a peer master is still +being processed or slave xfer is started by a peer very after it +queues a master command. + +To prevent state corruption in these cases, this patch adds the +'pending' state of master and its handling code so that the pending +master xfer can be continued after slave mode session. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 119 ++++++++++++++++++++++++++++++---------- + 1 file changed, 91 insertions(+), 28 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 8dc9161ced38..d11b2ea97259 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -117,6 +117,7 @@ + + enum aspeed_i2c_master_state { + ASPEED_I2C_MASTER_INACTIVE, ++ ASPEED_I2C_MASTER_PENDING, + ASPEED_I2C_MASTER_START, + ASPEED_I2C_MASTER_TX_FIRST, + ASPEED_I2C_MASTER_TX, +@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { + }; + + enum aspeed_i2c_slave_state { +- ASPEED_I2C_SLAVE_STOP, ++ ASPEED_I2C_SLAVE_INACTIVE, + ASPEED_I2C_SLAVE_START, + ASPEED_I2C_SLAVE_READ_REQUESTED, + ASPEED_I2C_SLAVE_READ_PROCESSED, + ASPEED_I2C_SLAVE_WRITE_REQUESTED, + ASPEED_I2C_SLAVE_WRITE_RECEIVED, ++ ASPEED_I2C_SLAVE_STOP, + }; + + struct aspeed_i2c_bus { +@@ -156,6 +158,8 @@ struct aspeed_i2c_bus { + int cmd_err; + /* Protected only by i2c_lock_bus */ + int master_xfer_result; ++ /* Multi-master */ ++ bool multi_master; + #if IS_ENABLED(CONFIG_I2C_SLAVE) + struct i2c_client *slave; + enum aspeed_i2c_slave_state slave_state; +@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + } + + /* Slave is not currently active, irq was for someone else. */ +- if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) ++ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) + return irq_handled; + + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", +@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; + bus->slave_state = ASPEED_I2C_SLAVE_STOP; + } +- if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { ++ if (irq_status & ASPEED_I2CD_INTR_TX_NAK && ++ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { + irq_handled |= ASPEED_I2CD_INTR_TX_NAK; + bus->slave_state = ASPEED_I2C_SLAVE_STOP; + } +- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) +- irq_handled |= ASPEED_I2CD_INTR_TX_ACK; + + switch (bus->slave_state) { + case ASPEED_I2C_SLAVE_READ_REQUESTED: +- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) ++ if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) + dev_err(bus->dev, "Unexpected ACK on read request.\n"); + bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); +@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); + break; + case ASPEED_I2C_SLAVE_READ_PROCESSED: +- if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) ++ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { + dev_err(bus->dev, + "Expected ACK after processed read.\n"); ++ break; ++ } ++ irq_handled |= ASPEED_I2CD_INTR_TX_ACK; + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); + writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); + writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); +@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + break; + case ASPEED_I2C_SLAVE_STOP: + i2c_slave_event(slave, I2C_SLAVE_STOP, &value); ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; ++ break; ++ case ASPEED_I2C_SLAVE_START: ++ /* Slave was just started. Waiting for the next event. */; + break; + default: +- dev_err(bus->dev, "unhandled slave_state: %d\n", ++ dev_err(bus->dev, "unknown slave_state: %d\n", + bus->slave_state); ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + break; + } + +@@ -328,7 +339,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) + struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; + u8 slave_addr = i2c_8bit_addr_from_msg(msg); + +- bus->master_state = ASPEED_I2C_MASTER_START; ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ /* ++ * If it's requested in the middle of a slave session, set the master ++ * state to 'pending' then H/W will continue handling this master ++ * command when the bus comes back to idle state. ++ */ ++ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) ++ bus->master_state = ASPEED_I2C_MASTER_PENDING; ++ else ++#endif /* CONFIG_I2C_SLAVE */ ++ bus->master_state = ASPEED_I2C_MASTER_START; + bus->buf_index = 0; + + if (msg->flags & I2C_M_RD) { +@@ -384,10 +405,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; + goto out_complete; +- } else { +- /* Master is not currently active, irq was for someone else. */ +- if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) +- goto out_no_complete; + } + + /* +@@ -399,12 +416,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + if (ret) { + dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", + irq_status); +- bus->cmd_err = ret; +- bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); +- goto out_complete; ++ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { ++ bus->cmd_err = ret; ++ bus->master_state = ASPEED_I2C_MASTER_INACTIVE; ++ goto out_complete; ++ } + } + ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ /* ++ * A pending master command will be started by H/W when the bus comes ++ * back to idle state after completing a slave operation so change the ++ * master state from 'pending' to 'start' at here if slave is inactive. ++ */ ++ if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { ++ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) ++ goto out_no_complete; ++ ++ bus->master_state = ASPEED_I2C_MASTER_START; ++ } ++#endif /* CONFIG_I2C_SLAVE */ ++ ++ /* Master is not currently active, irq was for someone else. */ ++ if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || ++ bus->master_state == ASPEED_I2C_MASTER_PENDING) ++ goto out_no_complete; ++ + /* We are in an invalid state; reset bus to a known state. */ + if (!bus->msgs) { + dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n", +@@ -423,6 +461,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + * then update the state and handle the new state below. + */ + if (bus->master_state == ASPEED_I2C_MASTER_START) { ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ /* ++ * If a peer master starts a xfer very after it queues a master ++ * command, change its state to 'pending' then H/W will continue ++ * the queued master xfer just after completing the slave mode ++ * session. ++ */ ++ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { ++ bus->master_state = ASPEED_I2C_MASTER_PENDING; ++ dev_dbg(bus->dev, ++ "master goes pending due to a slave start\n"); ++ goto out_no_complete; ++ } ++#endif /* CONFIG_I2C_SLAVE */ + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { + bus->cmd_err = -ENXIO; +@@ -566,7 +618,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) + * interrupt bits. Each case needs to be handled using corresponding + * handlers depending on the current state. + */ +- if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { ++ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && ++ bus->master_state != ASPEED_I2C_MASTER_PENDING) { + irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); + irq_remaining &= ~irq_handled; + if (irq_remaining) +@@ -601,15 +654,14 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + { + struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); + unsigned long time_left, flags; +- int ret = 0; ++ int ret; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; + +- /* If bus is busy, attempt recovery. We assume a single master +- * environment. +- */ +- if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { ++ /* If bus is busy in a single master environment, attempt recovery. */ ++ if (!bus->multi_master && ++ (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS)) { + spin_unlock_irqrestore(&bus->lock, flags); + ret = aspeed_i2c_recover_bus(bus); + if (ret) +@@ -629,10 +681,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + time_left = wait_for_completion_timeout(&bus->cmd_complete, + bus->adap.timeout); + +- if (time_left == 0) ++ if (time_left == 0) { ++ /* ++ * If timed out and bus is still busy in a multi master ++ * environment, attempt recovery at here. ++ */ ++ if (bus->multi_master && ++ (readl(bus->base + ASPEED_I2C_CMD_REG) & ++ ASPEED_I2CD_BUS_BUSY_STS)) ++ ret = aspeed_i2c_recover_bus(bus); ++ + return -ETIMEDOUT; +- else +- return bus->master_xfer_result; ++ } ++ ++ return bus->master_xfer_result; + } + + static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) +@@ -672,7 +734,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) + __aspeed_i2c_reg_slave(bus, client->addr); + + bus->slave = client; +- bus->slave_state = ASPEED_I2C_SLAVE_STOP; ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + spin_unlock_irqrestore(&bus->lock, flags); + + return 0; +@@ -827,7 +889,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + if (ret < 0) + return ret; + +- if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) ++ if (of_property_read_bool(pdev->dev.of_node, "multi-master")) ++ bus->multi_master = true; ++ else + fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; + + /* Enable Master Mode */ +@@ -930,7 +994,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; + bus->adap.retries = 0; +- bus->adap.timeout = 5 * HZ; + bus->adap.algo = &aspeed_i2c_algo; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch new file mode 100644 index 000000000..b735ab38b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch @@ -0,0 +1,151 @@ +From 577b65960842f4098cdfc85a311261477c051d84 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 29 Jun 2018 11:00:02 -0700 +Subject: [PATCH] Add dump debug code into I2C drivers + +This commit enables dump debug of master and slave I2C drivers. +This is only for downstream debug purpose so it shouldn't go to +the upstream. + +Usage (in case of bus 5 for an example): +echo 5 > /sys/module/i2c_aspeed/parameters/dump_debug_bus_id +echo 1 > /sys/module/i2c_aspeed/parameters/dump_debug +echo 5 > /sys/module/i2c_slave_mqueue/parameters/dump_debug_bus_id +echo 1 > /sys/module/i2c_slave_mqueue/parameters/dump_debug + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 25 +++++++++++++++++++++++++ + drivers/i2c/i2c-slave-mqueue.c | 24 ++++++++++++++++++++++++ + 2 files changed, 49 insertions(+) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index d11b2ea97259..506d867b43d9 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -166,6 +166,19 @@ struct aspeed_i2c_bus { + #endif /* CONFIG_I2C_SLAVE */ + }; + ++static bool dump_debug __read_mostly; ++static int dump_debug_bus_id __read_mostly; ++ ++#define I2C_HEX_DUMP(bus, addr, flags, buf, len) \ ++ if (dump_debug && bus->adap.nr == dump_debug_bus_id) { \ ++ char dump_info[100] = {0,}; \ ++ snprintf(dump_info, sizeof(dump_info), \ ++ "%s (bus_id:%d, addr:0x%02x, flags:0x%02x): ", \ ++ __func__, bus->adap.nr, addr, flags); \ ++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \ ++ buf, len, true); \ ++ } ++ + static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus); + + static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) +@@ -655,6 +668,7 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); + unsigned long time_left, flags; + int ret; ++ int i; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; +@@ -694,6 +708,11 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + return -ETIMEDOUT; + } + ++ for (i = 0; i < num; i++) { ++ I2C_HEX_DUMP(bus, msgs[i].addr, msgs[i].flags, ++ msgs[i].buf, msgs[i].len); ++ } ++ + return bus->master_xfer_result; + } + +@@ -1061,6 +1080,12 @@ static struct platform_driver aspeed_i2c_bus_driver = { + }; + module_platform_driver(aspeed_i2c_bus_driver); + ++module_param_named(dump_debug, dump_debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing"); ++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, ++ S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing"); ++ + MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>"); + MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); + MODULE_LICENSE("GPL v2"); +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +index 6014bca0ff2a..0140c0dc4c03 100644 +--- a/drivers/i2c/i2c-slave-mqueue.c ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -21,6 +21,7 @@ struct mq_msg { + struct mq_queue { + struct bin_attribute bin; + struct kernfs_node *kn; ++ struct i2c_client *client; + + spinlock_t lock; /* spinlock for queue index handling */ + int in; +@@ -31,6 +32,19 @@ struct mq_queue { + struct mq_msg *queue; + }; + ++static bool dump_debug __read_mostly; ++static int dump_debug_bus_id __read_mostly; ++ ++#define I2C_HEX_DUMP(client, buf, len) \ ++ if (dump_debug && client->adapter->nr == dump_debug_bus_id) { \ ++ char dump_info[100] = {0,}; \ ++ snprintf(dump_info, sizeof(dump_info), \ ++ "%s (bus_id:%d, addr:0x%02x): ", \ ++ __func__, client->adapter->nr, client->addr); \ ++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \ ++ buf, len, true); \ ++ } ++ + static int i2c_slave_mqueue_callback(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) + { +@@ -49,6 +63,7 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client, + case I2C_SLAVE_WRITE_RECEIVED: + if (msg->len < MQ_MSGBUF_SIZE) { + msg->buf[msg->len++] = *val; ++ I2C_HEX_DUMP(client, val, 1); + } else { + dev_err(&client->dev, "message is truncated!\n"); + mq->truncated = 1; +@@ -101,6 +116,7 @@ static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, + if (msg->len <= count) { + ret = msg->len; + memcpy(buf, msg->buf, ret); ++ I2C_HEX_DUMP(mq->client, buf, ret); + } else { + ret = -EOVERFLOW; /* Drop this HUGE one. */ + } +@@ -131,6 +147,8 @@ static int i2c_slave_mqueue_probe(struct i2c_client *client, + + BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); + ++ mq->client = client; ++ + buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, + GFP_KERNEL); + if (!buf) +@@ -212,6 +230,12 @@ static struct i2c_driver i2c_slave_mqueue_driver = { + }; + module_i2c_driver(i2c_slave_mqueue_driver); + ++module_param_named(dump_debug, dump_debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing"); ++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, ++ S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing"); ++ + MODULE_LICENSE("GPL v2"); + MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); + MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch new file mode 100644 index 000000000..8c9d2dce0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch @@ -0,0 +1,132 @@ +From 7baa65c9bf638265874838401e27a7b6179559ff Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Wed, 2 Jan 2019 15:06:43 +0800 +Subject: [PATCH] Add high speed baud rate support for UART + +In order to support high speed baud rate(921600 bps), +the default UART clock(24MHz) needs to be switched +to 192MHz(from USB2.0 port1 PHY). + +Create a new 192M Hz clock and assign it to uart, +based on uart clock source configuration in SCU4C. + +bootloader(u-boot) will set SCU4C based on the environment configuration + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + drivers/clk/clk-aspeed.c | 41 +++++++++++++++++++++++++++----- + include/dt-bindings/clock/aspeed-clock.h | 2 ++ + 2 files changed, 37 insertions(+), 6 deletions(-) + +diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c +index 5961367..3bbb4fb 100644 +--- a/drivers/clk/clk-aspeed.c ++++ b/drivers/clk/clk-aspeed.c +@@ -14,7 +14,9 @@ + + #include <dt-bindings/clock/aspeed-clock.h> + +-#define ASPEED_NUM_CLKS 36 ++#define ASPEED_NUM_CLKS ASPEED_CLK_MAX ++#define UART_HIGH_SPEED_CLK 192000000 ++#define UART_LOW_SPEED_CLK 24000000 + + #define ASPEED_RESET2_OFFSET 32 + +@@ -28,6 +30,12 @@ + #define AST2400_HPLL_BYPASS_EN BIT(17) + #define ASPEED_MISC_CTRL 0x2c + #define UART_DIV13_EN BIT(12) ++#define ASPEED_MISC2_CTRL 0x4c ++#define UART1_HS_CLK_EN BIT(24) ++#define UART2_HS_CLK_EN BIT(25) ++#define UART3_HS_CLK_EN BIT(26) ++#define UART4_HS_CLK_EN BIT(27) ++#define UART5_HS_CLK_EN BIT(28) + #define ASPEED_STRAP 0x70 + #define CLKIN_25MHZ_EN BIT(23) + #define AST2400_CLK_SOURCE_SEL BIT(18) +@@ -425,7 +433,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) + struct aspeed_reset *ar; + struct regmap *map; + struct clk_hw *hw; +- u32 val, rate; ++ u32 val, uart_clock_div; + int i, ret; + + map = syscon_node_to_regmap(dev->of_node); +@@ -460,15 +468,23 @@ static int aspeed_clk_probe(struct platform_device *pdev) + /* UART clock div13 setting */ + regmap_read(map, ASPEED_MISC_CTRL, &val); + if (val & UART_DIV13_EN) +- rate = 24000000 / 13; ++ uart_clock_div = 13; + else +- rate = 24000000; ++ uart_clock_div = 1; ++ + /* TODO: Find the parent data for the uart clock */ +- hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate); ++ hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, ++ UART_LOW_SPEED_CLK / uart_clock_div); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_UART] = hw; + ++ hw = clk_hw_register_fixed_rate(dev, "uart-hs", "usb-port1-gate", 0, ++ UART_HIGH_SPEED_CLK / uart_clock_div); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_UART_HS] = hw; ++ + /* + * Memory controller (M-PLL) PLL. This clock is configured by the + * bootloader, and is exposed to Linux as a read-only clock rate. +@@ -534,9 +550,22 @@ static int aspeed_clk_probe(struct platform_device *pdev) + * Video Engine (ECLK) mux and clock divider + */ + ++ /* Get the uart clock source configuration from SCU4C*/ ++ regmap_read(map, ASPEED_MISC2_CTRL, &val); + for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) { + const struct aspeed_gate_data *gd = &aspeed_gates[i]; + u32 gate_flags; ++ char *parent_name; ++ ++ /* For uart, needs to adjust the clock based on SCU4C value */ ++ if ((i == ASPEED_CLK_GATE_UART1CLK && (val & UART1_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART2CLK && (val & UART2_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART5CLK && (val & UART5_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART3CLK && (val & UART3_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART4CLK && (val & UART4_HS_CLK_EN))) ++ parent_name = "uart-hs"; ++ else ++ parent_name = gd->parent_name; + + /* Special case: the USB port 1 clock (bit 14) is always + * working the opposite way from the other ones. +@@ -544,7 +573,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) + gate_flags = (gd->clock_idx == 14) ? 0 : CLK_GATE_SET_TO_DISABLE; + hw = aspeed_clk_hw_register_gate(dev, + gd->name, +- gd->parent_name, ++ parent_name, + gd->flags, + map, + gd->clock_idx, +diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h +index f437386..3358795 100644 +--- a/include/dt-bindings/clock/aspeed-clock.h ++++ b/include/dt-bindings/clock/aspeed-clock.h +@@ -39,6 +39,8 @@ + #define ASPEED_CLK_BCLK 33 + #define ASPEED_CLK_MPLL 34 + #define ASPEED_CLK_24M 35 ++#define ASPEED_CLK_UART_HS 36 ++#define ASPEED_CLK_MAX 37 + + #define ASPEED_RESET_XDMA 0 + #define ASPEED_RESET_MCTP 1 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch new file mode 100644 index 000000000..539c976c7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch @@ -0,0 +1,556 @@ +From e39e3a3e54cbe8e5a39b4148a9232f4570d009a6 Mon Sep 17 00:00:00 2001 +From: Oskar Senft <osk@google.com> +Date: Wed, 8 Aug 2018 10:15:05 -0400 +Subject: [PATCH] misc: aspeed: Add Aspeed UART routing control driver. + +This driver adds sysfs files that allow the BMC userspace to configure +how UARTs and physical serial I/O ports are routed. + +Tested: Checked correct behavior (both read & write) on TYAN S7106 +board by manually changing routing settings and confirming that bits +flow as expected. Tested for UART1 and UART3 as this board doesn't have +the other UARTs wired up in a testable way. + +Signed-off-by: Oskar Senft <osk@google.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + .../ABI/stable/sysfs-driver-aspeed-uart-routing | 14 + + Documentation/misc-devices/aspeed-uart-routing.txt | 49 +++ + arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 4 + + arch/arm/boot/dts/aspeed-g5.dtsi | 6 + + drivers/misc/Kconfig | 6 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-uart-routing.c | 383 +++++++++++++++++++++ + 7 files changed, 463 insertions(+) + create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing + create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt + create mode 100644 drivers/misc/aspeed-uart-routing.c + +diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing +new file mode 100644 +index 000000000000..5068737d9c12 +--- /dev/null ++++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing +@@ -0,0 +1,14 @@ ++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io* ++Date: August 2018 ++Contact: Oskar Senft <osk@google.com> ++Description: Configures the input source for the specific physical ++ serial I/O port. ++Users: OpenBMC. Proposed changes should be mailed to ++ openbmc@lists.ozlabs.org ++ ++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart* ++Date: August 2018 ++Contact: Oskar Senft <osk@google.com> ++Description: Configures the input source for the specific UART. ++Users: OpenBMC. Proposed changes should be mailed to ++ openbmc@lists.ozlabs.org +diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt +new file mode 100644 +index 000000000000..afaf17cb7eda +--- /dev/null ++++ b/Documentation/misc-devices/aspeed-uart-routing.txt +@@ -0,0 +1,49 @@ ++Kernel driver aspeed-uart-routing ++================================= ++ ++Supported chips: ++ASPEED AST2500 ++ ++Author: ++Google LLC ++ ++Description ++----------- ++ ++The Aspeed AST2500 allows to dynamically route the inputs for the built-in ++UARTS and physical serial I/O ports. ++ ++This allows, for example, to connect the output of UART to another UART. ++This can be used to enable host<->BMC communication via UARTs, e.g. to allow ++access to the host's serial console. ++ ++This driver is for the BMC side. The sysfs files allow the BMC userspace ++which owns the system configuration policy, to configure how UARTs and ++physical serial I/O ports are routed. ++ ++The driver provides the following files in sysfs: ++uart1 Configure the input signal to UART1. ++uart2 Configure the input signal to UART2. ++uart3 Configure the input signal to UART3. ++uart4 Configure the input signal to UART4. ++uart5 Configure the input signal to UART5. ++io1 Configure the input signal to physical serial port 1. ++io2 Configure the input signal to physical serial port 2. ++io3 Configure the input signal to physical serial port 3. ++io4 Configure the input signal to physical serial port 4. ++io5 Configure the input signal to physical serial port 5. ++ ++When read, each file shows the list of available options with the currently ++selected option marked by square brackets "[]". The list of available options ++depends on the selected file. ++ ++Example: ++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++[io1] io2 io3 io4 uart2 uart3 uart4 io6 ++ ++In this case, UART1 gets its input signal from IO1 (physical serial port 1). ++ ++$ echo -n "uart3" \ ++ >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++io1 io2 io3 io4 uart2 [uart3] uart4 io6 +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts +index 655bb37e422f..eb05f5a2c480 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts +@@ -174,6 +174,10 @@ + status = "okay"; + }; + ++&uart_routing { ++ status = "okay"; ++}; ++ + &mac1 { + status = "okay"; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 3bb31c1daf9d..92843cc1a8f4 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -482,6 +482,12 @@ + status = "disabled"; + }; + }; ++ ++ uart_routing: uart_routing@9c { ++ compatible = "aspeed,ast2500-uart-routing"; ++ reg = <0x9c 0x4>; ++ status = "disabled"; ++ }; + }; + + peci: bus@1e78b000 { +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index f2062546250c..8e2fc51dcc44 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -537,6 +537,12 @@ config MISC_RTSX + tristate + default MISC_RTSX_PCI || MISC_RTSX_USB + ++config ASPEED_UART_ROUTING ++ tristate "Aspeed ast2500 UART routing control" ++ help ++ If you want to configure UART routing on Aspeed BMC platforms, enable ++ this option. ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index bb89694e6b4b..0f00eb63556c 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ + obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o ++obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o + obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o +diff --git a/drivers/misc/aspeed-uart-routing.c b/drivers/misc/aspeed-uart-routing.c +new file mode 100644 +index 000000000000..21ef5d98c317 +--- /dev/null ++++ b/drivers/misc/aspeed-uart-routing.c +@@ -0,0 +1,383 @@ ++/* ++ * UART Routing driver for Aspeed AST2500 ++ * ++ * Copyright (c) 2018 Google LLC ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++ ++/* The Aspeed AST2500 allows to dynamically route the inputs for the built-in ++ * UARTS and physical serial I/O ports. ++ * ++ * This allows, for example, to connect the output of UART to another UART. ++ * This can be used to enable host<->BMC communication via UARTs, e.g. to allow ++ * access to the host's serial console. ++ * ++ * This driver is for the BMC side. The sysfs files allow the BMC userspace ++ * which owns the system configuration policy, to configure how UARTs and ++ * physical serial I/O ports are routed. ++ */ ++ ++#define ASPEED_HICRA_IO1 "io1" ++#define ASPEED_HICRA_IO2 "io2" ++#define ASPEED_HICRA_IO3 "io3" ++#define ASPEED_HICRA_IO4 "io4" ++#define ASPEED_HICRA_IO5 "io5" ++#define ASPEED_HICRA_IO6 "io6" ++#define ASPEED_HICRA_UART1 "uart1" ++#define ASPEED_HICRA_UART2 "uart2" ++#define ASPEED_HICRA_UART3 "uart3" ++#define ASPEED_HICRA_UART4 "uart4" ++#define ASPEED_HICRA_UART5 "uart5" ++ ++struct aspeed_uart_routing { ++ struct device *dev; ++ void __iomem *regs; ++ spinlock_t lock; ++}; ++ ++struct aspeed_uart_routing_selector { ++ struct device_attribute dev_attr; ++ int shift; ++ int mask; ++ const char * const options[]; ++}; ++ ++#define to_routing_selector(_dev_attr) \ ++ container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr) ++ ++ ++static ssize_t aspeed_uart_routing_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf); ++ ++static ssize_t aspeed_uart_routing_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count); ++ ++#define ROUTING_ATTR(_name) { \ ++ .attr = {.name = _name, \ ++ .mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO) }, \ ++ .show = aspeed_uart_routing_show, \ ++ .store = aspeed_uart_routing_store, \ ++} ++ ++static struct aspeed_uart_routing_selector uart5_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART5), ++ .shift = 28, ++ .mask = 0xf, ++ .options = { ++ ASPEED_HICRA_IO5, // 0 ++ ASPEED_HICRA_IO1, // 1 ++ ASPEED_HICRA_IO2, // 2 ++ ASPEED_HICRA_IO3, // 3 ++ ASPEED_HICRA_IO4, // 4 ++ ASPEED_HICRA_UART1, // 5 ++ ASPEED_HICRA_UART2, // 6 ++ ASPEED_HICRA_UART3, // 7 ++ ASPEED_HICRA_UART4, // 8 ++ ASPEED_HICRA_IO6, // 9 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart4_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART4), ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO4, // 0 ++ ASPEED_HICRA_IO1, // 1 ++ ASPEED_HICRA_IO2, // 2 ++ ASPEED_HICRA_IO3, // 3 ++ ASPEED_HICRA_UART1, // 4 ++ ASPEED_HICRA_UART2, // 5 ++ ASPEED_HICRA_UART3, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart3_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART3), ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO3, // 0 ++ ASPEED_HICRA_IO4, // 1 ++ ASPEED_HICRA_IO1, // 2 ++ ASPEED_HICRA_IO2, // 3 ++ ASPEED_HICRA_UART4, // 4 ++ ASPEED_HICRA_UART1, // 5 ++ ASPEED_HICRA_UART2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart2_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART2), ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO2, // 0 ++ ASPEED_HICRA_IO3, // 1 ++ ASPEED_HICRA_IO4, // 2 ++ ASPEED_HICRA_IO1, // 3 ++ ASPEED_HICRA_UART3, // 4 ++ ASPEED_HICRA_UART4, // 5 ++ ASPEED_HICRA_UART1, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart1_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART1), ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO1, // 0 ++ ASPEED_HICRA_IO2, // 1 ++ ASPEED_HICRA_IO3, // 2 ++ ASPEED_HICRA_IO4, // 3 ++ ASPEED_HICRA_UART2, // 4 ++ ASPEED_HICRA_UART3, // 5 ++ ASPEED_HICRA_UART4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io5_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO5), ++ .shift = 12, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART5, // 0 ++ ASPEED_HICRA_UART1, // 1 ++ ASPEED_HICRA_UART2, // 2 ++ ASPEED_HICRA_UART3, // 3 ++ ASPEED_HICRA_UART4, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO3, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io4_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO4), ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART4, // 0 ++ ASPEED_HICRA_UART5, // 1 ++ ASPEED_HICRA_UART1, // 2 ++ ASPEED_HICRA_UART2, // 3 ++ ASPEED_HICRA_UART3, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io3_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO3), ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART3, // 0 ++ ASPEED_HICRA_UART4, // 1 ++ ASPEED_HICRA_UART5, // 2 ++ ASPEED_HICRA_UART1, // 3 ++ ASPEED_HICRA_UART2, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io2_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO2), ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART2, // 0 ++ ASPEED_HICRA_UART3, // 1 ++ ASPEED_HICRA_UART4, // 2 ++ ASPEED_HICRA_UART5, // 3 ++ ASPEED_HICRA_UART1, // 4 ++ ASPEED_HICRA_IO3, // 5 ++ ASPEED_HICRA_IO4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io1_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO1), ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART1, // 0 ++ ASPEED_HICRA_UART2, // 1 ++ ASPEED_HICRA_UART3, // 2 ++ ASPEED_HICRA_UART4, // 3 ++ ASPEED_HICRA_UART5, // 4 ++ ASPEED_HICRA_IO3, // 5 ++ ASPEED_HICRA_IO4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++ ++static struct attribute *aspeed_uart_routing_attrs[] = { ++ &uart1_sel.dev_attr.attr, ++ &uart2_sel.dev_attr.attr, ++ &uart3_sel.dev_attr.attr, ++ &uart4_sel.dev_attr.attr, ++ &uart5_sel.dev_attr.attr, ++ &io1_sel.dev_attr.attr, ++ &io2_sel.dev_attr.attr, ++ &io3_sel.dev_attr.attr, ++ &io4_sel.dev_attr.attr, ++ &io5_sel.dev_attr.attr, ++ NULL, ++}; ++ ++static const struct attribute_group aspeed_uart_routing_attr_group = { ++ .attrs = aspeed_uart_routing_attrs, ++}; ++ ++static ssize_t aspeed_uart_routing_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); ++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); ++ int val, pos, len; ++ ++ val = (readl(uart_routing->regs) >> sel->shift) & sel->mask; ++ ++ len = 0; ++ for (pos = 0; sel->options[pos] != NULL; ++pos) { ++ if (pos == val) { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "[%s] ", sel->options[pos]); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "%s ", sel->options[pos]); ++ } ++ } ++ ++ if (val >= pos) { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "[unknown(%d)]", val); ++ } ++ ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, "\n"); ++ ++ return len; ++} ++ ++static ssize_t aspeed_uart_routing_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); ++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); ++ int val; ++ u32 reg; ++ ++ val = match_string(sel->options, -1, buf); ++ if (val < 0) { ++ dev_err(dev, "invalid value \"%s\"\n", buf); ++ return -EINVAL; ++ } ++ ++ spin_lock(&uart_routing->lock); ++ reg = readl(uart_routing->regs); ++ // Zero out existing value in specified bits. ++ reg &= ~(sel->mask << sel->shift); ++ // Set new value in specified bits. ++ reg |= (val & sel->mask) << sel->shift; ++ writel(reg, uart_routing->regs); ++ spin_unlock(&uart_routing->lock); ++ ++ return count; ++} ++ ++static int aspeed_uart_routing_probe(struct platform_device *pdev) ++{ ++ struct aspeed_uart_routing *uart_routing; ++ struct resource *res; ++ int rc; ++ ++ uart_routing = devm_kzalloc(&pdev->dev, ++ sizeof(*uart_routing), ++ GFP_KERNEL); ++ if (!uart_routing) ++ return -ENOMEM; ++ ++ spin_lock_init(&uart_routing->lock); ++ uart_routing->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ uart_routing->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(uart_routing->regs)) ++ return PTR_ERR(uart_routing->regs); ++ ++ rc = sysfs_create_group(&uart_routing->dev->kobj, ++ &aspeed_uart_routing_attr_group); ++ if (rc < 0) ++ return rc; ++ ++ platform_set_drvdata(pdev, uart_routing); ++ ++ return 0; ++} ++ ++static int aspeed_uart_routing_remove(struct platform_device *pdev) ++{ ++ struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev); ++ ++ sysfs_remove_group(&uart_routing->dev->kobj, ++ &aspeed_uart_routing_attr_group); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_uart_routing_table[] = { ++ { .compatible = "aspeed,ast2500-uart-routing" }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_uart_routing_driver = { ++ .driver = { ++ .name = "aspeed-uart-routing", ++ .of_match_table = aspeed_uart_routing_table, ++ }, ++ .probe = aspeed_uart_routing_probe, ++ .remove = aspeed_uart_routing_remove, ++}; ++ ++module_platform_driver(aspeed_uart_routing_driver); ++ ++MODULE_AUTHOR("Oskar Senft <osk@google.com>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Driver to configure Aspeed UART routing"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch new file mode 100644 index 000000000..eef3bee6f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch @@ -0,0 +1,85 @@ +From 9c509b9450f641c169ee3aeb60e398c43810dcb2 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 3 Oct 2018 10:17:58 -0700 +Subject: [PATCH] arm: dts: adpeed: Swap the mac nodes numbering + +This patch swaps the numbering of mac0 and mac1 to make a dedicated +nic get assigned the first ethernet device number. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 16 ++++++++-------- + arch/arm/boot/dts/aspeed-g5.dtsi | 16 ++++++++-------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 22eab8a952ed..004bbb08dd4a 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -101,14 +101,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -117,6 +109,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2400-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 92843cc1a8f4..30a7f349feeb 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -142,14 +142,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -158,6 +150,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2500-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch new file mode 100644 index 000000000..51ddbb18e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch @@ -0,0 +1,249 @@ +From 1d459c15998c9a79ba7a758cef6129ed29f3b958 Mon Sep 17 00:00:00 2001 +From: cyang29 <cheng.c.yang@intel.com> +Date: Fri, 9 Nov 2018 10:24:37 +0800 +Subject: [PATCH] Implement a memory driver share memory + +Implement a memory driver for BMC to access VGA share memory. +The driver is used by MDRV2. In MDRV2 BIOS will send whole +SMBIOS table to VGA memory and BMC can get the table from VGA +memory through this driver. + +Signed-off-by: cyang29 <cheng.c.yang@intel.com> +--- + .../devicetree/bindings/misc/vga-shared-memory.txt | 20 +++ + drivers/misc/Kconfig | 10 ++ + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-vga-sharedmem.c | 164 +++++++++++++++++++++ + 4 files changed, 195 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/vga-shared-memory.txt + create mode 100644 drivers/misc/aspeed-vga-sharedmem.c + +diff --git a/Documentation/devicetree/bindings/misc/vga-shared-memory.txt b/Documentation/devicetree/bindings/misc/vga-shared-memory.txt +new file mode 100644 +index 000000000000..03f57c53e844 +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/vga-shared-memory.txt +@@ -0,0 +1,20 @@ ++* Aspeed VGA shared memory driver ++ ++Aspeed VGA shared memory driver allow user to read data from AST2500 ++VGA memory. This driver is required by ManagedDataRegionlV2 ++specification. In the spec, BIOS will transfer whole SMBIOS table to ++VGA memroy and BMC get the table from VGA memory. 0penBMC project do ++not allow to use /dev/mem for security concerns. To get the data in ++VGA shared memory in user space, implement this driver only allowed ++user to mmap limited memory area. ++ ++Required properties: ++- compatible: "aspeed,ast2500-vga-sharedmem" ++ - aspeed,ast2500-vga-sharedmem: Aspeed AST2500 family ++- reg: Should contain VGA shared memory start address and length ++ ++Example: ++vga-shared-memory { ++ compatible = "aspeed,ast2500-vga-sharedmem"; ++ reg = <0x9ff00000 0x100000>; ++}; +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 8e2fc51dcc44..1279a9674537 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -543,6 +543,16 @@ config ASPEED_UART_ROUTING + If you want to configure UART routing on Aspeed BMC platforms, enable + this option. + ++config ASPEED_VGA_SHAREDMEM ++ tristate "Aspeed VGA Shared memory" ++ depends on (ARCH_ASPEED || COMPILE_TEST) ++ help ++ To access VGA shared memory on Aspeed BMC, enable this option. ++ This driver used by ManagedDataRegionlV2 specification. In the ++ specification, BIOS will transfer whole SMBIOS table to VGA ++ memory, and BMC can get the table from VGA memory through this ++ driver. ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 0f00eb63556c..f4951a6e435b 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -62,3 +62,4 @@ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-$(CONFIG_MISC_RTSX) += cardreader/ ++obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o +diff --git a/drivers/misc/aspeed-vga-sharedmem.c b/drivers/misc/aspeed-vga-sharedmem.c +new file mode 100644 +index 000000000000..76f60cd67d3a +--- /dev/null ++++ b/drivers/misc/aspeed-vga-sharedmem.c +@@ -0,0 +1,164 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2018 Intel Corporation ++ * VGA Shared Memory driver for Aspeed AST2500 ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/miscdevice.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++ ++#define SHAREDMEM_NAME "vgasharedmem" ++ ++struct aspeed_vga_sharedmem { ++ struct miscdevice miscdev; ++ unsigned int addr; ++ unsigned int size; ++ bool mmap_enable; ++}; ++ ++static struct aspeed_vga_sharedmem *file_sharemem(struct file *file) ++{ ++ return container_of(file->private_data, ++ struct aspeed_vga_sharedmem, miscdev); ++} ++ ++static int vga_open(struct inode *inode, struct file *file) ++{ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file); ++ ++ if (!vga_sharedmem->mmap_enable) ++ return -EPERM; ++ ++ return 0; ++} ++ ++static int vga_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ vma->vm_flags = (vma->vm_flags & (~VM_WRITE)); ++ remap_pfn_range(vma, vma->vm_start, vga_sharedmem->addr >> PAGE_SHIFT, ++ vga_sharedmem->size, vma->vm_page_prot); ++ return 0; ++} ++ ++static ssize_t enable_mmap_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", vga_sharedmem->mmap_enable); ++} ++ ++static ssize_t enable_mmap_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = ++ dev_get_drvdata(dev); ++ bool val; ++ ++ if (kstrtobool(buf, &val)) ++ return -EINVAL; ++ ++ vga_sharedmem->mmap_enable = val; ++ ++ return count; ++} ++static DEVICE_ATTR_RW(enable_mmap); ++ ++static struct attribute *sharedmem_attrs[] = { ++ &dev_attr_enable_mmap.attr, ++ NULL ++}; ++ ++static const struct attribute_group sharedmem_attr_group = { ++ .attrs = sharedmem_attrs, ++}; ++ ++static const struct attribute_group *sharedmem_attr_groups[] = { ++ &sharedmem_attr_group, ++ NULL ++}; ++ ++static const struct file_operations vga_sharedmem_fops = { ++ .owner = THIS_MODULE, ++ .open = vga_open, ++ .mmap = vga_mmap, ++}; ++ ++static struct miscdevice vga_sharedmem_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = SHAREDMEM_NAME, ++ .fops = &vga_sharedmem_fops, ++ .groups = sharedmem_attr_groups, ++}; ++ ++static int vga_sharedmem_probe(struct platform_device *pdev) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem; ++ struct device *dev = &pdev->dev; ++ u32 reg[2]; ++ struct resource *rc; ++ ++ vga_sharedmem = devm_kzalloc(dev, sizeof(*vga_sharedmem), GFP_KERNEL); ++ if (!vga_sharedmem) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, vga_sharedmem); ++ ++ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!rc) { ++ dev_err(dev, "Couldn't read size device-tree property\n"); ++ return -ENXIO; ++ } ++ ++ vga_sharedmem->addr = rc->start; ++ vga_sharedmem->size = resource_size(rc); ++ vga_sharedmem->mmap_enable = true; ++ ++ vga_sharedmem->miscdev = vga_sharedmem_miscdev; ++ ++ return misc_register(&vga_sharedmem->miscdev); ++} ++ ++static int vga_sharedmem_remove(struct platform_device *pdev) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = ++ dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&vga_sharedmem->miscdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id vga_sharedmem_match[] = { ++ { .compatible = "aspeed,ast2500-vga-sharedmem", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, vga_sharedmem_match); ++ ++static struct platform_driver vga_sharedmem_driver = { ++ .driver = { ++ .name = "VGA-SHAREDMEM", ++ .of_match_table = vga_sharedmem_match, ++ }, ++ .probe = vga_sharedmem_probe, ++ .remove = vga_sharedmem_remove, ++}; ++ ++module_platform_driver(vga_sharedmem_driver); ++ ++MODULE_AUTHOR("Yang Cheng <cheng.c.yang@intel.com>"); ++MODULE_DESCRIPTION("Shared VGA memory"); ++MODULE_LICENSE("GPL v2"); +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch new file mode 100644 index 000000000..83717369c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch @@ -0,0 +1,1425 @@ +From 58c3299017c5e6022fb2a2a74b662b2a4c0306f5 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 20 Nov 2018 10:14:47 -0800 +Subject: [PATCH] net/ncsi: backport ncsi patches + +net/ncsi: Allow enabling multiple packages & channels + +This series extends the NCSI driver to configure multiple packages +and/or channels simultaneously. Since the RFC series this includes a few +extra changes to fix areas in the driver that either made this harder or +were roadblocks due to deviations from the NCSI specification. + +Patches 1 & 2 fix two issues where the driver made assumptions about the +capabilities of the NCSI topology. +Patches 3 & 4 change some internal semantics slightly to make multi-mode +easier. +Patch 5 introduces a cleaner way of reconfiguring the NCSI configuration +and keeping track of channel states. +Patch 6 implements the main multi-package/multi-channel configuration, +configured via the Netlink interface. + +Readers who have an interesting NCSI setup - especially multi-package +with HWA - please test! I think I've covered all permutations but I +don't have infinite hardware to test on. + +net/ncsi: Don't enable all channels when HWA available + +NCSI hardware arbitration allows multiple packages to be enabled at once +and share the same wiring. If the NCSI driver recognises that HWA is +available it unconditionally enables all packages and channels; but that +is a configuration decision rather than something required by HWA. +Additionally the current implementation will not failover on link events +which can cause connectivity to be lost unless the interface is manually +bounced. + +Retain basic HWA support but remove the separate configuration path to +enable all channels, leaving this to be handled by a later +implementation. + +net/ncsi: Probe single packages to avoid conflict + +Currently the NCSI driver sends a select-package command to all possible +packages simultaneously to discover what packages are available. However +at this stage in the probe process the driver does not know if +hardware arbitration is available: if it isn't then this process could +cause collisions on the RMII bus when packages try to respond. + +Update the probe loop to probe each package one by one, and once +complete check if HWA is universally supported. + +net/ncsi: Don't deselect package in suspend if active + +When a package is deselected all channels of that package cease +communication. If there are other channels active on the package of the +suspended channel this will disable them as well, so only send a +deselect-package command if no other channels are active. + +net/ncsi: Don't mark configured channels inactive + +The concepts of a channel being 'active' and it having link are slightly +muddled in the NCSI driver. Tweak this slightly so that +NCSI_CHANNEL_ACTIVE represents a channel that has been configured and +enabled, and NCSI_CHANNEL_INACTIVE represents a de-configured channel. +This distinction is important because a channel can be 'active' but have +its link down; in this case the channel may still need to be configured +so that it may receive AEN link-state-change packets. + +net/ncsi: Reset channel state in ncsi_start_dev() + +When the NCSI driver is stopped with ncsi_stop_dev() the channel +monitors are stopped and the state set to "inactive". However the +channels are still configured and active from the perspective of the +network controller. We should suspend each active channel but in the +context of ncsi_stop_dev() the transmit queue has been or is about to be +stopped so we won't have time to do so. + +Instead when ncsi_start_dev() is called if the NCSI topology has already +been probed then call ncsi_reset_dev() to suspend any channels that were +previously active. This resets the network controller to a known state, +provides an up to date view of channel link state, and makes sure that +mode flags such as NCSI_MODE_TX_ENABLE are properly reset. + +In addition to ncsi_start_dev() use ncsi_reset_dev() in ncsi-netlink.c +to update the channel configuration more cleanly. + +net/ncsi: Configure multi-package, multi-channel modes with failover + +This patch extends the ncsi-netlink interface with two new commands and +three new attributes to configure multiple packages and/or channels at +once, and configure specific failover modes. + +NCSI_CMD_SET_PACKAGE mask and NCSI_CMD_SET_CHANNEL_MASK set a whitelist +of packages or channels allowed to be configured with the +NCSI_ATTR_PACKAGE_MASK and NCSI_ATTR_CHANNEL_MASK attributes +respectively. If one of these whitelists is set only packages or +channels matching the whitelist are considered for the channel queue in +ncsi_choose_active_channel(). + +These commands may also use the NCSI_ATTR_MULTI_FLAG to signal that +multiple packages or channels may be configured simultaneously. NCSI +hardware arbitration (HWA) must be available in order to enable +multi-package mode. Multi-channel mode is always available. + +If the NCSI_ATTR_CHANNEL_ID attribute is present in the +NCSI_CMD_SET_CHANNEL_MASK command the it sets the preferred channel as +with the NCSI_CMD_SET_INTERFACE command. The combination of preferred +channel and channel whitelist defines a primary channel and the allowed +failover channels. +If the NCSI_ATTR_MULTI_FLAG attribute is also present then the preferred +channel is configured for Tx/Rx and the other channels are enabled only +for Rx. + +Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + include/uapi/linux/ncsi.h | 15 ++ + net/ncsi/internal.h | 19 +- + net/ncsi/ncsi-aen.c | 75 +++++-- + net/ncsi/ncsi-manage.c | 522 ++++++++++++++++++++++++++++++++-------------- + net/ncsi/ncsi-netlink.c | 233 ++++++++++++++++++--- + net/ncsi/ncsi-rsp.c | 2 +- + 6 files changed, 660 insertions(+), 206 deletions(-) + +diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h +index 0a26a5576645..a3f87c54fdb3 100644 +--- a/include/uapi/linux/ncsi.h ++++ b/include/uapi/linux/ncsi.h +@@ -26,6 +26,12 @@ + * @NCSI_CMD_SEND_CMD: send NC-SI command to network card. + * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID + * and NCSI_ATTR_CHANNEL_ID. ++ * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages. ++ * Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK. ++ * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels. ++ * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and ++ * NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets ++ * the primary channel. + * @NCSI_CMD_MAX: highest command number + */ + enum ncsi_nl_commands { +@@ -34,6 +40,8 @@ enum ncsi_nl_commands { + NCSI_CMD_SET_INTERFACE, + NCSI_CMD_CLEAR_INTERFACE, + NCSI_CMD_SEND_CMD, ++ NCSI_CMD_SET_PACKAGE_MASK, ++ NCSI_CMD_SET_CHANNEL_MASK, + + __NCSI_CMD_AFTER_LAST, + NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 +@@ -48,6 +56,10 @@ enum ncsi_nl_commands { + * @NCSI_ATTR_PACKAGE_ID: package ID + * @NCSI_ATTR_CHANNEL_ID: channel ID + * @NCSI_ATTR_DATA: command payload ++ * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with ++ * NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK. ++ * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages. ++ * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels. + * @NCSI_ATTR_MAX: highest attribute number + */ + enum ncsi_nl_attrs { +@@ -57,6 +69,9 @@ enum ncsi_nl_attrs { + NCSI_ATTR_PACKAGE_ID, + NCSI_ATTR_CHANNEL_ID, + NCSI_ATTR_DATA, ++ NCSI_ATTR_MULTI_FLAG, ++ NCSI_ATTR_PACKAGE_MASK, ++ NCSI_ATTR_CHANNEL_MASK, + + __NCSI_ATTR_AFTER_LAST, + NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 +diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h +index 1dae77c54009..9e3642b802c4 100644 +--- a/net/ncsi/internal.h ++++ b/net/ncsi/internal.h +@@ -222,6 +222,10 @@ struct ncsi_package { + unsigned int channel_num; /* Number of channels */ + struct list_head channels; /* List of chanels */ + struct list_head node; /* Form list of packages */ ++ ++ bool multi_channel; /* Enable multiple channels */ ++ u32 channel_whitelist; /* Channels to configure */ ++ struct ncsi_channel *preferred_channel; /* Primary channel */ + }; + + struct ncsi_request { +@@ -287,16 +291,16 @@ struct ncsi_dev_priv { + #define NCSI_DEV_PROBED 1 /* Finalized NCSI topology */ + #define NCSI_DEV_HWA 2 /* Enabled HW arbitration */ + #define NCSI_DEV_RESHUFFLE 4 ++#define NCSI_DEV_RESET 8 /* Reset state of NC */ + unsigned int gma_flag; /* OEM GMA flag */ + spinlock_t lock; /* Protect the NCSI device */ + #if IS_ENABLED(CONFIG_IPV6) + unsigned int inet6_addr_num; /* Number of IPv6 addresses */ + #endif ++ unsigned int package_probe_id;/* Current ID during probe */ + unsigned int package_num; /* Number of packages */ + struct list_head packages; /* List of packages */ + struct ncsi_channel *hot_channel; /* Channel was ever active */ +- struct ncsi_package *force_package; /* Force a specific package */ +- struct ncsi_channel *force_channel; /* Force a specific channel */ + struct ncsi_request requests[256]; /* Request table */ + unsigned int request_id; /* Last used request ID */ + #define NCSI_REQ_START_IDX 1 +@@ -309,6 +313,9 @@ struct ncsi_dev_priv { + struct list_head node; /* Form NCSI device list */ + #define NCSI_MAX_VLAN_VIDS 15 + struct list_head vlan_vids; /* List of active VLAN IDs */ ++ ++ bool multi_package; /* Enable multiple packages */ ++ u32 package_whitelist; /* Packages to configure */ + }; + + struct ncsi_cmd_arg { +@@ -341,6 +348,7 @@ extern spinlock_t ncsi_dev_lock; + list_for_each_entry_rcu(nc, &np->channels, node) + + /* Resources */ ++int ncsi_reset_dev(struct ncsi_dev *nd); + void ncsi_start_channel_monitor(struct ncsi_channel *nc); + void ncsi_stop_channel_monitor(struct ncsi_channel *nc); + struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, +@@ -361,6 +369,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, + void ncsi_free_request(struct ncsi_request *nr); + struct ncsi_dev *ncsi_find_dev(struct net_device *dev); + int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); ++bool ncsi_channel_has_link(struct ncsi_channel *channel); ++bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, ++ struct ncsi_channel *channel); ++int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, ++ struct ncsi_package *np, ++ struct ncsi_channel *disable, ++ struct ncsi_channel *enable); + + /* Packet handlers */ + u32 ncsi_calculate_checksum(unsigned char *data, int len); +diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c +index 25e483e8278b..26d67e27551f 100644 +--- a/net/ncsi/ncsi-aen.c ++++ b/net/ncsi/ncsi-aen.c +@@ -50,13 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h, + static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, + struct ncsi_aen_pkt_hdr *h) + { +- struct ncsi_aen_lsc_pkt *lsc; +- struct ncsi_channel *nc; ++ struct ncsi_channel *nc, *tmp; + struct ncsi_channel_mode *ncm; +- bool chained; +- int state; + unsigned long old_data, data; ++ struct ncsi_aen_lsc_pkt *lsc; ++ struct ncsi_package *np; ++ bool had_link, has_link; + unsigned long flags; ++ bool chained; ++ int state; + + /* Find the NCSI channel */ + ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); +@@ -73,6 +75,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, + ncm->data[2] = data; + ncm->data[4] = ntohl(lsc->oem_status); + ++ had_link = !!(old_data & 0x1); ++ has_link = !!(data & 0x1); ++ + netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", + nc->id, data & 0x1 ? "up" : "down"); + +@@ -80,22 +85,60 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, + state = nc->state; + spin_unlock_irqrestore(&nc->lock, flags); + +- if (!((old_data ^ data) & 0x1) || chained) +- return 0; +- if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) && +- !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1))) ++ if (state == NCSI_CHANNEL_INACTIVE) ++ netdev_warn(ndp->ndev.dev, ++ "NCSI: Inactive channel %u received AEN!\n", ++ nc->id); ++ ++ if ((had_link == has_link) || chained) + return 0; + +- if (!(ndp->flags & NCSI_DEV_HWA) && +- state == NCSI_CHANNEL_ACTIVE) +- ndp->flags |= NCSI_DEV_RESHUFFLE; ++ if (!ndp->multi_package && !nc->package->multi_channel) { ++ if (had_link) { ++ ndp->flags |= NCSI_DEV_RESHUFFLE; ++ ncsi_stop_channel_monitor(nc); ++ spin_lock_irqsave(&ndp->lock, flags); ++ list_add_tail_rcu(&nc->link, &ndp->channel_queue); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return ncsi_process_next_channel(ndp); ++ } ++ /* Configured channel came up */ ++ return 0; ++ } + +- ncsi_stop_channel_monitor(nc); +- spin_lock_irqsave(&ndp->lock, flags); +- list_add_tail_rcu(&nc->link, &ndp->channel_queue); +- spin_unlock_irqrestore(&ndp->lock, flags); ++ if (had_link) { ++ ncm = &nc->modes[NCSI_MODE_TX_ENABLE]; ++ if (ncsi_channel_is_last(ndp, nc)) { ++ /* No channels left, reconfigure */ ++ return ncsi_reset_dev(&ndp->ndev); ++ } else if (ncm->enable) { ++ /* Need to failover Tx channel */ ++ ncsi_update_tx_channel(ndp, nc->package, nc, NULL); ++ } ++ } else if (has_link && nc->package->preferred_channel == nc) { ++ /* Return Tx to preferred channel */ ++ ncsi_update_tx_channel(ndp, nc->package, NULL, nc); ++ } else if (has_link) { ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ NCSI_FOR_EACH_CHANNEL(np, tmp) { ++ /* Enable Tx on this channel if the current Tx ++ * channel is down. ++ */ ++ ncm = &tmp->modes[NCSI_MODE_TX_ENABLE]; ++ if (ncm->enable && ++ !ncsi_channel_has_link(tmp)) { ++ ncsi_update_tx_channel(ndp, nc->package, ++ tmp, nc); ++ break; ++ } ++ } ++ } ++ } + +- return ncsi_process_next_channel(ndp); ++ /* Leave configured channels active in a multi-channel scenario so ++ * AEN events are still received. ++ */ ++ return 0; + } + + static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, +diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c +index bfc43b28c7a6..92e59f07f9a7 100644 +--- a/net/ncsi/ncsi-manage.c ++++ b/net/ncsi/ncsi-manage.c +@@ -28,6 +28,29 @@ + LIST_HEAD(ncsi_dev_list); + DEFINE_SPINLOCK(ncsi_dev_lock); + ++bool ncsi_channel_has_link(struct ncsi_channel *channel) ++{ ++ return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1); ++} ++ ++bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, ++ struct ncsi_channel *channel) ++{ ++ struct ncsi_package *np; ++ struct ncsi_channel *nc; ++ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) ++ NCSI_FOR_EACH_CHANNEL(np, nc) { ++ if (nc == channel) ++ continue; ++ if (nc->state == NCSI_CHANNEL_ACTIVE && ++ ncsi_channel_has_link(nc)) ++ return false; ++ } ++ ++ return true; ++} ++ + static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) + { + struct ncsi_dev *nd = &ndp->ndev; +@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) + continue; + } + +- if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { ++ if (ncsi_channel_has_link(nc)) { + spin_unlock_irqrestore(&nc->lock, flags); + nd->link_up = 1; + goto report; +@@ -113,10 +136,8 @@ static void ncsi_channel_monitor(struct timer_list *t) + default: + netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n", + nc->id); +- if (!(ndp->flags & NCSI_DEV_HWA)) { +- ncsi_report_link(ndp, true); +- ndp->flags |= NCSI_DEV_RESHUFFLE; +- } ++ ncsi_report_link(ndp, true); ++ ndp->flags |= NCSI_DEV_RESHUFFLE; + + ncsi_stop_channel_monitor(nc); + +@@ -269,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp, + np->ndp = ndp; + spin_lock_init(&np->lock); + INIT_LIST_HEAD(&np->channels); ++ np->channel_whitelist = UINT_MAX; + + spin_lock_irqsave(&ndp->lock, flags); + tmp = ncsi_find_package(ndp, id); +@@ -442,12 +464,14 @@ static void ncsi_request_timeout(struct timer_list *t) + static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) + { + struct ncsi_dev *nd = &ndp->ndev; +- struct ncsi_package *np = ndp->active_package; +- struct ncsi_channel *nc = ndp->active_channel; ++ struct ncsi_package *np; ++ struct ncsi_channel *nc, *tmp; + struct ncsi_cmd_arg nca; + unsigned long flags; + int ret; + ++ np = ndp->active_package; ++ nc = ndp->active_channel; + nca.ndp = ndp; + nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN; + switch (nd->state) { +@@ -523,6 +547,15 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) + if (ret) + goto error; + ++ NCSI_FOR_EACH_CHANNEL(np, tmp) { ++ /* If there is another channel active on this package ++ * do not deselect the package. ++ */ ++ if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) { ++ nd->state = ncsi_dev_state_suspend_done; ++ break; ++ } ++ } + break; + case ncsi_dev_state_suspend_deselect: + ndp->pending_req_num = 1; +@@ -541,8 +574,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) + spin_lock_irqsave(&nc->lock, flags); + nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); +- ncsi_process_next_channel(ndp); +- ++ if (ndp->flags & NCSI_DEV_RESET) ++ ncsi_reset_dev(nd); ++ else ++ ncsi_process_next_channel(ndp); + break; + default: + netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n", +@@ -717,13 +752,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id) + + #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ + ++/* Determine if a given channel from the channel_queue should be used for Tx */ ++static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp, ++ struct ncsi_channel *nc) ++{ ++ struct ncsi_channel_mode *ncm; ++ struct ncsi_channel *channel; ++ struct ncsi_package *np; ++ ++ /* Check if any other channel has Tx enabled; a channel may have already ++ * been configured and removed from the channel queue. ++ */ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ if (!ndp->multi_package && np != nc->package) ++ continue; ++ NCSI_FOR_EACH_CHANNEL(np, channel) { ++ ncm = &channel->modes[NCSI_MODE_TX_ENABLE]; ++ if (ncm->enable) ++ return false; ++ } ++ } ++ ++ /* This channel is the preferred channel and has link */ ++ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) { ++ np = channel->package; ++ if (np->preferred_channel && ++ ncsi_channel_has_link(np->preferred_channel)) { ++ return np->preferred_channel == nc; ++ } ++ } ++ ++ /* This channel has link */ ++ if (ncsi_channel_has_link(nc)) ++ return true; ++ ++ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) ++ if (ncsi_channel_has_link(channel)) ++ return false; ++ ++ /* No other channel has link; default to this one */ ++ return true; ++} ++ ++/* Change the active Tx channel in a multi-channel setup */ ++int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, ++ struct ncsi_package *package, ++ struct ncsi_channel *disable, ++ struct ncsi_channel *enable) ++{ ++ struct ncsi_cmd_arg nca; ++ struct ncsi_channel *nc; ++ struct ncsi_package *np; ++ int ret = 0; ++ ++ if (!package->multi_channel && !ndp->multi_package) ++ netdev_warn(ndp->ndev.dev, ++ "NCSI: Trying to update Tx channel in single-channel mode\n"); ++ nca.ndp = ndp; ++ nca.req_flags = 0; ++ ++ /* Find current channel with Tx enabled */ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ if (disable) ++ break; ++ if (!ndp->multi_package && np != package) ++ continue; ++ ++ NCSI_FOR_EACH_CHANNEL(np, nc) ++ if (nc->modes[NCSI_MODE_TX_ENABLE].enable) { ++ disable = nc; ++ break; ++ } ++ } ++ ++ /* Find a suitable channel for Tx */ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ if (enable) ++ break; ++ if (!ndp->multi_package && np != package) ++ continue; ++ if (!(ndp->package_whitelist & (0x1 << np->id))) ++ continue; ++ ++ if (np->preferred_channel && ++ ncsi_channel_has_link(np->preferred_channel)) { ++ enable = np->preferred_channel; ++ break; ++ } ++ ++ NCSI_FOR_EACH_CHANNEL(np, nc) { ++ if (!(np->channel_whitelist & 0x1 << nc->id)) ++ continue; ++ if (nc->state != NCSI_CHANNEL_ACTIVE) ++ continue; ++ if (ncsi_channel_has_link(nc)) { ++ enable = nc; ++ break; ++ } ++ } ++ } ++ ++ if (disable == enable) ++ return -1; ++ ++ if (!enable) ++ return -1; ++ ++ if (disable) { ++ nca.channel = disable->id; ++ nca.package = disable->package->id; ++ nca.type = NCSI_PKT_CMD_DCNT; ++ ret = ncsi_xmit_cmd(&nca); ++ if (ret) ++ netdev_err(ndp->ndev.dev, ++ "Error %d sending DCNT\n", ++ ret); ++ } ++ ++ netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id); ++ ++ nca.channel = enable->id; ++ nca.package = enable->package->id; ++ nca.type = NCSI_PKT_CMD_ECNT; ++ ret = ncsi_xmit_cmd(&nca); ++ if (ret) ++ netdev_err(ndp->ndev.dev, ++ "Error %d sending ECNT\n", ++ ret); ++ ++ return ret; ++} ++ + static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + { +- struct ncsi_dev *nd = &ndp->ndev; +- struct net_device *dev = nd->dev; + struct ncsi_package *np = ndp->active_package; + struct ncsi_channel *nc = ndp->active_channel; + struct ncsi_channel *hot_nc = NULL; ++ struct ncsi_dev *nd = &ndp->ndev; ++ struct net_device *dev = nd->dev; + struct ncsi_cmd_arg nca; + unsigned char index; + unsigned long flags; +@@ -845,20 +1011,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + } else if (nd->state == ncsi_dev_state_config_ebf) { + nca.type = NCSI_PKT_CMD_EBF; + nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; +- nd->state = ncsi_dev_state_config_ecnt; ++ if (ncsi_channel_is_tx(ndp, nc)) ++ nd->state = ncsi_dev_state_config_ecnt; ++ else ++ nd->state = ncsi_dev_state_config_ec; + #if IS_ENABLED(CONFIG_IPV6) + if (ndp->inet6_addr_num > 0 && + (nc->caps[NCSI_CAP_GENERIC].cap & + NCSI_CAP_GENERIC_MC)) + nd->state = ncsi_dev_state_config_egmf; +- else +- nd->state = ncsi_dev_state_config_ecnt; + } else if (nd->state == ncsi_dev_state_config_egmf) { + nca.type = NCSI_PKT_CMD_EGMF; + nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap; +- nd->state = ncsi_dev_state_config_ecnt; ++ if (ncsi_channel_is_tx(ndp, nc)) ++ nd->state = ncsi_dev_state_config_ecnt; ++ else ++ nd->state = ncsi_dev_state_config_ec; + #endif /* CONFIG_IPV6 */ + } else if (nd->state == ncsi_dev_state_config_ecnt) { ++ if (np->preferred_channel && ++ nc != np->preferred_channel) ++ netdev_info(ndp->ndev.dev, ++ "NCSI: Tx failed over to channel %u\n", ++ nc->id); + nca.type = NCSI_PKT_CMD_ECNT; + nd->state = ncsi_dev_state_config_ec; + } else if (nd->state == ncsi_dev_state_config_ec) { +@@ -889,6 +1064,16 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n", + nc->id); + spin_lock_irqsave(&nc->lock, flags); ++ nc->state = NCSI_CHANNEL_ACTIVE; ++ ++ if (ndp->flags & NCSI_DEV_RESET) { ++ /* A reset event happened during config, start it now */ ++ nc->reconfigure_needed = false; ++ spin_unlock_irqrestore(&nc->lock, flags); ++ ncsi_reset_dev(nd); ++ break; ++ } ++ + if (nc->reconfigure_needed) { + /* This channel's configuration has been updated + * part-way during the config state - start the +@@ -909,10 +1094,8 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + + if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { + hot_nc = nc; +- nc->state = NCSI_CHANNEL_ACTIVE; + } else { + hot_nc = NULL; +- nc->state = NCSI_CHANNEL_INACTIVE; + netdev_dbg(ndp->ndev.dev, + "NCSI: channel %u link down after config\n", + nc->id); +@@ -940,43 +1123,35 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + + static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) + { +- struct ncsi_package *np, *force_package; +- struct ncsi_channel *nc, *found, *hot_nc, *force_channel; ++ struct ncsi_channel *nc, *found, *hot_nc; + struct ncsi_channel_mode *ncm; +- unsigned long flags; ++ unsigned long flags, cflags; ++ struct ncsi_package *np; ++ bool with_link; + + spin_lock_irqsave(&ndp->lock, flags); + hot_nc = ndp->hot_channel; +- force_channel = ndp->force_channel; +- force_package = ndp->force_package; + spin_unlock_irqrestore(&ndp->lock, flags); + +- /* Force a specific channel whether or not it has link if we have been +- * configured to do so +- */ +- if (force_package && force_channel) { +- found = force_channel; +- ncm = &found->modes[NCSI_MODE_LINK]; +- if (!(ncm->data[2] & 0x1)) +- netdev_info(ndp->ndev.dev, +- "NCSI: Channel %u forced, but it is link down\n", +- found->id); +- goto out; +- } +- +- /* The search is done once an inactive channel with up +- * link is found. ++ /* By default the search is done once an inactive channel with up ++ * link is found, unless a preferred channel is set. ++ * If multi_package or multi_channel are configured all channels in the ++ * whitelist are added to the channel queue. + */ + found = NULL; ++ with_link = false; + NCSI_FOR_EACH_PACKAGE(ndp, np) { +- if (ndp->force_package && np != ndp->force_package) ++ if (!(ndp->package_whitelist & (0x1 << np->id))) + continue; + NCSI_FOR_EACH_CHANNEL(np, nc) { +- spin_lock_irqsave(&nc->lock, flags); ++ if (!(np->channel_whitelist & (0x1 << nc->id))) ++ continue; ++ ++ spin_lock_irqsave(&nc->lock, cflags); + + if (!list_empty(&nc->link) || + nc->state != NCSI_CHANNEL_INACTIVE) { +- spin_unlock_irqrestore(&nc->lock, flags); ++ spin_unlock_irqrestore(&nc->lock, cflags); + continue; + } + +@@ -988,32 +1163,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) + + ncm = &nc->modes[NCSI_MODE_LINK]; + if (ncm->data[2] & 0x1) { +- spin_unlock_irqrestore(&nc->lock, flags); + found = nc; +- goto out; ++ with_link = true; + } + +- spin_unlock_irqrestore(&nc->lock, flags); ++ /* If multi_channel is enabled configure all valid ++ * channels whether or not they currently have link ++ * so they will have AENs enabled. ++ */ ++ if (with_link || np->multi_channel) { ++ spin_lock_irqsave(&ndp->lock, flags); ++ list_add_tail_rcu(&nc->link, ++ &ndp->channel_queue); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ netdev_dbg(ndp->ndev.dev, ++ "NCSI: Channel %u added to queue (link %s)\n", ++ nc->id, ++ ncm->data[2] & 0x1 ? "up" : "down"); ++ } ++ ++ spin_unlock_irqrestore(&nc->lock, cflags); ++ ++ if (with_link && !np->multi_channel) ++ break; + } ++ if (with_link && !ndp->multi_package) ++ break; + } + +- if (!found) { ++ if (list_empty(&ndp->channel_queue) && found) { ++ netdev_info(ndp->ndev.dev, ++ "NCSI: No channel with link found, configuring channel %u\n", ++ found->id); ++ spin_lock_irqsave(&ndp->lock, flags); ++ list_add_tail_rcu(&found->link, &ndp->channel_queue); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ } else if (!found) { + netdev_warn(ndp->ndev.dev, +- "NCSI: No channel found with link\n"); ++ "NCSI: No channel found to configure!\n"); + ncsi_report_link(ndp, true); + return -ENODEV; + } + +- ncm = &found->modes[NCSI_MODE_LINK]; +- netdev_dbg(ndp->ndev.dev, +- "NCSI: Channel %u added to queue (link %s)\n", +- found->id, ncm->data[2] & 0x1 ? "up" : "down"); +- +-out: +- spin_lock_irqsave(&ndp->lock, flags); +- list_add_tail_rcu(&found->link, &ndp->channel_queue); +- spin_unlock_irqrestore(&ndp->lock, flags); +- + return ncsi_process_next_channel(ndp); + } + +@@ -1050,35 +1242,6 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp) + return false; + } + +-static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp) +-{ +- struct ncsi_package *np; +- struct ncsi_channel *nc; +- unsigned long flags; +- +- /* Move all available channels to processing queue */ +- spin_lock_irqsave(&ndp->lock, flags); +- NCSI_FOR_EACH_PACKAGE(ndp, np) { +- NCSI_FOR_EACH_CHANNEL(np, nc) { +- WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE || +- !list_empty(&nc->link)); +- ncsi_stop_channel_monitor(nc); +- list_add_tail_rcu(&nc->link, &ndp->channel_queue); +- } +- } +- spin_unlock_irqrestore(&ndp->lock, flags); +- +- /* We can have no channels in extremely case */ +- if (list_empty(&ndp->channel_queue)) { +- netdev_err(ndp->ndev.dev, +- "NCSI: No available channels for HWA\n"); +- ncsi_report_link(ndp, false); +- return -ENOENT; +- } +- +- return ncsi_process_next_channel(ndp); +-} +- + static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) + { + struct ncsi_dev *nd = &ndp->ndev; +@@ -1110,70 +1273,28 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) + nd->state = ncsi_dev_state_probe_package; + break; + case ncsi_dev_state_probe_package: +- ndp->pending_req_num = 16; ++ ndp->pending_req_num = 1; + +- /* Select all possible packages */ + nca.type = NCSI_PKT_CMD_SP; + nca.bytes[0] = 1; ++ nca.package = ndp->package_probe_id; + nca.channel = NCSI_RESERVED_CHANNEL; +- for (index = 0; index < 8; index++) { +- nca.package = index; +- ret = ncsi_xmit_cmd(&nca); +- if (ret) +- goto error; +- } +- +- /* Disable all possible packages */ +- nca.type = NCSI_PKT_CMD_DP; +- for (index = 0; index < 8; index++) { +- nca.package = index; +- ret = ncsi_xmit_cmd(&nca); +- if (ret) +- goto error; +- } +- ++ ret = ncsi_xmit_cmd(&nca); ++ if (ret) ++ goto error; + nd->state = ncsi_dev_state_probe_channel; + break; + case ncsi_dev_state_probe_channel: +- if (!ndp->active_package) +- ndp->active_package = list_first_or_null_rcu( +- &ndp->packages, struct ncsi_package, node); +- else if (list_is_last(&ndp->active_package->node, +- &ndp->packages)) +- ndp->active_package = NULL; +- else +- ndp->active_package = list_next_entry( +- ndp->active_package, node); +- +- /* All available packages and channels are enumerated. The +- * enumeration happens for once when the NCSI interface is +- * started. So we need continue to start the interface after +- * the enumeration. +- * +- * We have to choose an active channel before configuring it. +- * Note that we possibly don't have active channel in extreme +- * situation. +- */ ++ ndp->active_package = ncsi_find_package(ndp, ++ ndp->package_probe_id); + if (!ndp->active_package) { +- ndp->flags |= NCSI_DEV_PROBED; +- if (ncsi_check_hwa(ndp)) +- ncsi_enable_hwa(ndp); +- else +- ncsi_choose_active_channel(ndp); +- return; ++ /* No response */ ++ nd->state = ncsi_dev_state_probe_dp; ++ schedule_work(&ndp->work); ++ break; + } +- +- /* Select the active package */ +- ndp->pending_req_num = 1; +- nca.type = NCSI_PKT_CMD_SP; +- nca.bytes[0] = 1; +- nca.package = ndp->active_package->id; +- nca.channel = NCSI_RESERVED_CHANNEL; +- ret = ncsi_xmit_cmd(&nca); +- if (ret) +- goto error; +- + nd->state = ncsi_dev_state_probe_cis; ++ schedule_work(&ndp->work); + break; + case ncsi_dev_state_probe_cis: + ndp->pending_req_num = NCSI_RESERVED_CHANNEL; +@@ -1222,22 +1343,35 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) + case ncsi_dev_state_probe_dp: + ndp->pending_req_num = 1; + +- /* Deselect the active package */ ++ /* Deselect the current package */ + nca.type = NCSI_PKT_CMD_DP; +- nca.package = ndp->active_package->id; ++ nca.package = ndp->package_probe_id; + nca.channel = NCSI_RESERVED_CHANNEL; + ret = ncsi_xmit_cmd(&nca); + if (ret) + goto error; + +- /* Scan channels in next package */ +- nd->state = ncsi_dev_state_probe_channel; ++ /* Probe next package */ ++ ndp->package_probe_id++; ++ if (ndp->package_probe_id >= 8) { ++ /* Probe finished */ ++ ndp->flags |= NCSI_DEV_PROBED; ++ break; ++ } ++ nd->state = ncsi_dev_state_probe_package; ++ ndp->active_package = NULL; + break; + default: + netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n", + nd->state); + } + ++ if (ndp->flags & NCSI_DEV_PROBED) { ++ /* Check if all packages have HWA support */ ++ ncsi_check_hwa(ndp); ++ ncsi_choose_active_channel(ndp); ++ } ++ + return; + error: + netdev_err(ndp->ndev.dev, +@@ -1556,6 +1690,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, + INIT_LIST_HEAD(&ndp->channel_queue); + INIT_LIST_HEAD(&ndp->vlan_vids); + INIT_WORK(&ndp->work, ncsi_dev_work); ++ ndp->package_whitelist = UINT_MAX; + + /* Initialize private NCSI device */ + spin_lock_init(&ndp->lock); +@@ -1592,26 +1727,19 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev); + int ncsi_start_dev(struct ncsi_dev *nd) + { + struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); +- int ret; + + if (nd->state != ncsi_dev_state_registered && + nd->state != ncsi_dev_state_functional) + return -ENOTTY; + + if (!(ndp->flags & NCSI_DEV_PROBED)) { ++ ndp->package_probe_id = 0; + nd->state = ncsi_dev_state_probe; + schedule_work(&ndp->work); + return 0; + } + +- if (ndp->flags & NCSI_DEV_HWA) { +- netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n"); +- ret = ncsi_enable_hwa(ndp); +- } else { +- ret = ncsi_choose_active_channel(ndp); +- } +- +- return ret; ++ return ncsi_reset_dev(nd); + } + EXPORT_SYMBOL_GPL(ncsi_start_dev); + +@@ -1624,7 +1752,10 @@ void ncsi_stop_dev(struct ncsi_dev *nd) + int old_state; + unsigned long flags; + +- /* Stop the channel monitor and reset channel's state */ ++ /* Stop the channel monitor on any active channels. Don't reset the ++ * channel state so we know which were active when ncsi_start_dev() ++ * is next called. ++ */ + NCSI_FOR_EACH_PACKAGE(ndp, np) { + NCSI_FOR_EACH_CHANNEL(np, nc) { + ncsi_stop_channel_monitor(nc); +@@ -1632,7 +1763,6 @@ void ncsi_stop_dev(struct ncsi_dev *nd) + spin_lock_irqsave(&nc->lock, flags); + chained = !list_empty(&nc->link); + old_state = nc->state; +- nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); + + WARN_ON_ONCE(chained || +@@ -1645,6 +1775,92 @@ void ncsi_stop_dev(struct ncsi_dev *nd) + } + EXPORT_SYMBOL_GPL(ncsi_stop_dev); + ++int ncsi_reset_dev(struct ncsi_dev *nd) ++{ ++ struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); ++ struct ncsi_channel *nc, *active, *tmp; ++ struct ncsi_package *np; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ndp->lock, flags); ++ ++ if (!(ndp->flags & NCSI_DEV_RESET)) { ++ /* Haven't been called yet, check states */ ++ switch (nd->state & ncsi_dev_state_major) { ++ case ncsi_dev_state_registered: ++ case ncsi_dev_state_probe: ++ /* Not even probed yet - do nothing */ ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return 0; ++ case ncsi_dev_state_suspend: ++ case ncsi_dev_state_config: ++ /* Wait for the channel to finish its suspend/config ++ * operation; once it finishes it will check for ++ * NCSI_DEV_RESET and reset the state. ++ */ ++ ndp->flags |= NCSI_DEV_RESET; ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return 0; ++ } ++ } else { ++ switch (nd->state) { ++ case ncsi_dev_state_suspend_done: ++ case ncsi_dev_state_config_done: ++ case ncsi_dev_state_functional: ++ /* Ok */ ++ break; ++ default: ++ /* Current reset operation happening */ ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return 0; ++ } ++ } ++ ++ if (!list_empty(&ndp->channel_queue)) { ++ /* Clear any channel queue we may have interrupted */ ++ list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link) ++ list_del_init(&nc->link); ++ } ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ active = NULL; ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ NCSI_FOR_EACH_CHANNEL(np, nc) { ++ spin_lock_irqsave(&nc->lock, flags); ++ ++ if (nc->state == NCSI_CHANNEL_ACTIVE) { ++ active = nc; ++ nc->state = NCSI_CHANNEL_INVISIBLE; ++ spin_unlock_irqrestore(&nc->lock, flags); ++ ncsi_stop_channel_monitor(nc); ++ break; ++ } ++ ++ spin_unlock_irqrestore(&nc->lock, flags); ++ } ++ if (active) ++ break; ++ } ++ ++ if (!active) { ++ /* Done */ ++ spin_lock_irqsave(&ndp->lock, flags); ++ ndp->flags &= ~NCSI_DEV_RESET; ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return ncsi_choose_active_channel(ndp); ++ } ++ ++ spin_lock_irqsave(&ndp->lock, flags); ++ ndp->flags |= NCSI_DEV_RESET; ++ ndp->active_channel = active; ++ ndp->active_package = active->package; ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ nd->state = ncsi_dev_state_suspend; ++ schedule_work(&ndp->work); ++ return 0; ++} ++ + void ncsi_unregister_dev(struct ncsi_dev *nd) + { + struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); +diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c +index 33314381b4f5..5d782445d2fc 100644 +--- a/net/ncsi/ncsi-netlink.c ++++ b/net/ncsi/ncsi-netlink.c +@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { + [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, + [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, + [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, ++ [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, ++ [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, ++ [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, + }; + + static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) +@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb, + nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); + if (nc->state == NCSI_CHANNEL_ACTIVE) + nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); +- if (ndp->force_channel == nc) ++ if (nc == nc->package->preferred_channel) + nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); + + nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); +@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb, + if (!pnest) + return -ENOMEM; + nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); +- if (ndp->force_package == np) ++ if ((0x1 << np->id) == ndp->package_whitelist) + nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); + cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); + if (!cnest) { +@@ -290,49 +293,58 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); + package = NULL; + +- spin_lock_irqsave(&ndp->lock, flags); +- + NCSI_FOR_EACH_PACKAGE(ndp, np) + if (np->id == package_id) + package = np; + if (!package) { + /* The user has set a package that does not exist */ +- spin_unlock_irqrestore(&ndp->lock, flags); + return -ERANGE; + } + + channel = NULL; +- if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { +- /* Allow any channel */ +- channel_id = NCSI_RESERVED_CHANNEL; +- } else { ++ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); + NCSI_FOR_EACH_CHANNEL(package, nc) +- if (nc->id == channel_id) ++ if (nc->id == channel_id) { + channel = nc; ++ break; ++ } ++ if (!channel) { ++ netdev_info(ndp->ndev.dev, ++ "NCSI: Channel %u does not exist!\n", ++ channel_id); ++ return -ERANGE; ++ } + } + +- if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { +- /* The user has set a channel that does not exist on this +- * package +- */ +- spin_unlock_irqrestore(&ndp->lock, flags); +- netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", +- channel_id); +- return -ERANGE; +- } +- +- ndp->force_package = package; +- ndp->force_channel = channel; ++ spin_lock_irqsave(&ndp->lock, flags); ++ ndp->package_whitelist = 0x1 << package->id; ++ ndp->multi_package = false; + spin_unlock_irqrestore(&ndp->lock, flags); + +- netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", +- package_id, channel_id, +- channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); ++ spin_lock_irqsave(&package->lock, flags); ++ package->multi_channel = false; ++ if (channel) { ++ package->channel_whitelist = 0x1 << channel->id; ++ package->preferred_channel = channel; ++ } else { ++ /* Allow any channel */ ++ package->channel_whitelist = UINT_MAX; ++ package->preferred_channel = NULL; ++ } ++ spin_unlock_irqrestore(&package->lock, flags); ++ ++ if (channel) ++ netdev_info(ndp->ndev.dev, ++ "Set package 0x%x, channel 0x%x as preferred\n", ++ package_id, channel_id); ++ else ++ netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", ++ package_id); + +- /* Bounce the NCSI channel to set changes */ +- ncsi_stop_dev(&ndp->ndev); +- ncsi_start_dev(&ndp->ndev); ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); + + return 0; + } +@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) + static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) + { + struct ncsi_dev_priv *ndp; ++ struct ncsi_package *np; + unsigned long flags; + + if (!info || !info->attrs) +@@ -353,16 +366,24 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) + if (!ndp) + return -ENODEV; + +- /* Clear any override */ ++ /* Reset any whitelists and disable multi mode */ + spin_lock_irqsave(&ndp->lock, flags); +- ndp->force_package = NULL; +- ndp->force_channel = NULL; ++ ndp->package_whitelist = UINT_MAX; ++ ndp->multi_package = false; + spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ spin_lock_irqsave(&np->lock, flags); ++ np->multi_channel = false; ++ np->channel_whitelist = UINT_MAX; ++ np->preferred_channel = NULL; ++ spin_unlock_irqrestore(&np->lock, flags); ++ } + netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); + +- /* Bounce the NCSI channel to set changes */ +- ncsi_stop_dev(&ndp->ndev); +- ncsi_start_dev(&ndp->ndev); ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); + + return 0; + } +@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev, + return nlmsg_unicast(net->genl_sock, skb, snd_portid); + } + ++static int ncsi_set_package_mask_nl(struct sk_buff *msg, ++ struct genl_info *info) ++{ ++ struct ncsi_dev_priv *ndp; ++ unsigned long flags; ++ int rc; ++ ++ if (!info || !info->attrs) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_IFINDEX]) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) ++ return -EINVAL; ++ ++ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), ++ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); ++ if (!ndp) ++ return -ENODEV; ++ ++ spin_lock_irqsave(&ndp->lock, flags); ++ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { ++ if (ndp->flags & NCSI_DEV_HWA) { ++ ndp->multi_package = true; ++ rc = 0; ++ } else { ++ netdev_err(ndp->ndev.dev, ++ "NCSI: Can't use multiple packages without HWA\n"); ++ rc = -EPERM; ++ } ++ } else { ++ ndp->multi_package = false; ++ rc = 0; ++ } ++ ++ if (!rc) ++ ndp->package_whitelist = ++ nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ if (!rc) { ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); ++ } ++ ++ return rc; ++} ++ ++static int ncsi_set_channel_mask_nl(struct sk_buff *msg, ++ struct genl_info *info) ++{ ++ struct ncsi_package *np, *package; ++ struct ncsi_channel *nc, *channel; ++ u32 package_id, channel_id; ++ struct ncsi_dev_priv *ndp; ++ unsigned long flags; ++ ++ if (!info || !info->attrs) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_IFINDEX]) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) ++ return -EINVAL; ++ ++ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), ++ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); ++ if (!ndp) ++ return -ENODEV; ++ ++ package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); ++ package = NULL; ++ NCSI_FOR_EACH_PACKAGE(ndp, np) ++ if (np->id == package_id) { ++ package = np; ++ break; ++ } ++ if (!package) ++ return -ERANGE; ++ ++ spin_lock_irqsave(&package->lock, flags); ++ ++ channel = NULL; ++ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { ++ channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); ++ NCSI_FOR_EACH_CHANNEL(np, nc) ++ if (nc->id == channel_id) { ++ channel = nc; ++ break; ++ } ++ if (!channel) { ++ spin_unlock_irqrestore(&package->lock, flags); ++ return -ERANGE; ++ } ++ netdev_dbg(ndp->ndev.dev, ++ "NCSI: Channel %u set as preferred channel\n", ++ channel->id); ++ } ++ ++ package->channel_whitelist = ++ nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); ++ if (package->channel_whitelist == 0) ++ netdev_dbg(ndp->ndev.dev, ++ "NCSI: Package %u set to all channels disabled\n", ++ package->id); ++ ++ package->preferred_channel = channel; ++ ++ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { ++ package->multi_channel = true; ++ netdev_info(ndp->ndev.dev, ++ "NCSI: Multi-channel enabled on package %u\n", ++ package_id); ++ } else { ++ package->multi_channel = false; ++ } ++ ++ spin_unlock_irqrestore(&package->lock, flags); ++ ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); ++ ++ return 0; ++} ++ + static const struct genl_ops ncsi_ops[] = { + { + .cmd = NCSI_CMD_PKG_INFO, +@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = { + .doit = ncsi_send_cmd_nl, + .flags = GENL_ADMIN_PERM, + }, ++ { ++ .cmd = NCSI_CMD_SET_PACKAGE_MASK, ++ .policy = ncsi_genl_policy, ++ .doit = ncsi_set_package_mask_nl, ++ .flags = GENL_ADMIN_PERM, ++ }, ++ { ++ .cmd = NCSI_CMD_SET_CHANNEL_MASK, ++ .policy = ncsi_genl_policy, ++ .doit = ncsi_set_channel_mask_nl, ++ .flags = GENL_ADMIN_PERM, ++ }, + }; + + static struct genl_family ncsi_genl_family __ro_after_init = { +diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c +index 77e07ba3f493..de7737a27889 100644 +--- a/net/ncsi/ncsi-rsp.c ++++ b/net/ncsi/ncsi-rsp.c +@@ -256,7 +256,7 @@ static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr) + if (!ncm->enable) + return 0; + +- ncm->enable = 1; ++ ncm->enable = 0; + return 0; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch new file mode 100644 index 000000000..02e423057 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch @@ -0,0 +1,1995 @@ +From feb75b023dfd0ebe3e8ca46f0e74603f07542c29 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 9 Nov 2018 11:32:27 -0800 +Subject: [PATCH] Add Aspeed Video Engine Driver + +The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs +can capture and compress video data from digital or analog sources. With +the Aspeed chip acting a service processor, the Video Engine can capture +the host processor graphics output. + +Add a V4L2 driver to capture video data and compress it to JPEG images. +Make the video frames available through the V4L2 streaming interface. + +media: platform: Fix missing spin_lock_init() + +The driver allocates the spinlock but not initialize it. +Use spin_lock_init() on it to initialize it correctly. + +This is detected by Coccinelle semantic patch. + +Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") + +Signed-off-by: Eddie James <eajames@linux.ibm.com> +Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com> +Reviewed-by: Rob Herring <robh@kernel.org> +Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> +Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> +--- + .../devicetree/bindings/media/aspeed-video.txt | 26 + + MAINTAINERS | 8 + + arch/arm/boot/dts/aspeed-g5.dtsi | 11 + + drivers/clk/clk-aspeed.c | 41 +- + drivers/media/platform/Kconfig | 9 + + drivers/media/platform/Makefile | 1 + + drivers/media/platform/aspeed-video.c | 1730 ++++++++++++++++++++ + include/dt-bindings/clock/aspeed-clock.h | 1 + + 8 files changed, 1825 insertions(+), 2 deletions(-) + create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt + create mode 100644 drivers/media/platform/aspeed-video.c + +diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt +new file mode 100644 +index 000000000000..78b464ae2672 +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/aspeed-video.txt +@@ -0,0 +1,26 @@ ++* Device tree bindings for Aspeed Video Engine ++ ++The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can ++capture and compress video data from digital or analog sources. ++ ++Required properties: ++ - compatible: "aspeed,ast2400-video-engine" or ++ "aspeed,ast2500-video-engine" ++ - reg: contains the offset and length of the VE memory region ++ - clocks: clock specifiers for the syscon clocks associated with ++ the VE (ordering must match the clock-names property) ++ - clock-names: "vclk" and "eclk" ++ - resets: reset specifier for the syscon reset associated with ++ the VE ++ - interrupts: the interrupt associated with the VE on this platform ++ ++Example: ++ ++video-engine@1e700000 { ++ compatible = "aspeed,ast2500-video-engine"; ++ reg = <0x1e700000 0x20000>; ++ clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>; ++ clock-names = "vclk", "eclk"; ++ resets = <&syscon ASPEED_RESET_VIDEO>; ++ interrupts = <7>; ++}; +diff --git a/MAINTAINERS b/MAINTAINERS +index b98a5763f6c1..cdd8f824b6da 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2358,6 +2358,14 @@ S: Maintained + F: Documentation/hwmon/asc7621 + F: drivers/hwmon/asc7621.c + ++ASPEED VIDEO ENGINE DRIVER ++M: Eddie James <eajames@linux.ibm.com> ++L: linux-media@vger.kernel.org ++L: openbmc@lists.ozlabs.org (moderated for non-subscribers) ++S: Maintained ++F: drivers/media/platform/aspeed-video.c ++F: Documentation/devicetree/bindings/media/aspeed-video.txt ++ + ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS + M: Corentin Chary <corentin.chary@gmail.com> + L: acpi4asus-user@lists.sourceforge.net +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 69330ae2efd3..946c13eaa1d4 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -243,6 +243,17 @@ + interrupts = <0x19>; + }; + ++ video: video@1e700000 { ++ compatible = "aspeed,ast2500-video-engine"; ++ reg = <0x1e700000 0x20000>; ++ clocks = <&syscon ASPEED_CLK_GATE_VCLK>, ++ <&syscon ASPEED_CLK_GATE_ECLK>; ++ clock-names = "vclk", "eclk"; ++ resets = <&syscon ASPEED_RESET_VIDEO>; ++ interrupts = <7>; ++ status = "disabled"; ++ }; ++ + adc: adc@1e6e9000 { + compatible = "aspeed,ast2500-adc"; + reg = <0x1e6e9000 0xb0>; +diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c +index 3bbb4fbf00c9..6cea55de485f 100644 +--- a/drivers/clk/clk-aspeed.c ++++ b/drivers/clk/clk-aspeed.c +@@ -95,7 +95,7 @@ struct aspeed_clk_gate { + /* TODO: ask Aspeed about the actual parent data */ + static const struct aspeed_gate_data aspeed_gates[] = { + /* clk rst name parent flags */ +- [ASPEED_CLK_GATE_ECLK] = { 0, -1, "eclk-gate", "eclk", 0 }, /* Video Engine */ ++ [ASPEED_CLK_GATE_ECLK] = { 0, 6, "eclk-gate", "eclk", 0 }, /* Video Engine */ + [ASPEED_CLK_GATE_GCLK] = { 1, 7, "gclk-gate", NULL, 0 }, /* 2D engine */ + [ASPEED_CLK_GATE_MCLK] = { 2, -1, "mclk-gate", "mpll", CLK_IS_CRITICAL }, /* SDRAM */ + [ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */ +@@ -121,6 +121,24 @@ static const struct aspeed_gate_data aspeed_gates[] = { + [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ + }; + ++static const char * const eclk_parent_names[] = { ++ "mpll", ++ "hpll", ++ "dpll", ++}; ++ ++static const struct clk_div_table ast2500_eclk_div_table[] = { ++ { 0x0, 2 }, ++ { 0x1, 2 }, ++ { 0x2, 3 }, ++ { 0x3, 4 }, ++ { 0x4, 5 }, ++ { 0x5, 6 }, ++ { 0x6, 7 }, ++ { 0x7, 8 }, ++ { 0 } ++}; ++ + static const struct clk_div_table ast2500_mac_div_table[] = { + { 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */ + { 0x1, 4 }, +@@ -200,18 +218,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val) + + struct aspeed_clk_soc_data { + const struct clk_div_table *div_table; ++ const struct clk_div_table *eclk_div_table; + const struct clk_div_table *mac_div_table; + struct clk_hw *(*calc_pll)(const char *name, u32 val); + }; + + static const struct aspeed_clk_soc_data ast2500_data = { + .div_table = ast2500_div_table, ++ .eclk_div_table = ast2500_eclk_div_table, + .mac_div_table = ast2500_mac_div_table, + .calc_pll = aspeed_ast2500_calc_pll, + }; + + static const struct aspeed_clk_soc_data ast2400_data = { + .div_table = ast2400_div_table, ++ .eclk_div_table = ast2400_div_table, + .mac_div_table = ast2400_div_table, + .calc_pll = aspeed_ast2400_calc_pll, + }; +@@ -325,6 +346,7 @@ static const u8 aspeed_resets[] = { + [ASPEED_RESET_PECI] = 10, + [ASPEED_RESET_I2C] = 2, + [ASPEED_RESET_AHB] = 1, ++ [ASPEED_RESET_VIDEO] = 6, + + /* + * SCUD4 resets start at an offset to separate them from +@@ -538,6 +560,22 @@ static int aspeed_clk_probe(struct platform_device *pdev) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_24M] = hw; + ++ hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names, ++ ARRAY_SIZE(eclk_parent_names), 0, ++ scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0, ++ &aspeed_clk_lock); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw; ++ ++ hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0, ++ scu_base + ASPEED_CLK_SELECTION, 28, ++ 3, 0, soc_data->eclk_div_table, ++ &aspeed_clk_lock); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw; ++ + /* + * TODO: There are a number of clocks that not included in this driver + * as more information is required: +@@ -547,7 +585,6 @@ static int aspeed_clk_probe(struct platform_device *pdev) + * RGMII + * RMII + * UART[1..5] clock source mux +- * Video Engine (ECLK) mux and clock divider + */ + + /* Get the uart clock source configuration from SCU4C*/ +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index 54fe90acb5b2..d6edf2d28f9b 100644 +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -32,6 +32,15 @@ source "drivers/media/platform/davinci/Kconfig" + + source "drivers/media/platform/omap/Kconfig" + ++config VIDEO_ASPEED ++ tristate "Aspeed AST2400 and AST2500 Video Engine driver" ++ depends on VIDEO_V4L2 ++ select VIDEOBUF2_DMA_CONTIG ++ help ++ Support for the Aspeed Video Engine (VE) embedded in the Aspeed ++ AST2400 and AST2500 SOCs. The VE can capture and compress video data ++ from digital or analog sources. ++ + config VIDEO_SH_VOU + tristate "SuperH VOU video output driver" + depends on MEDIA_CAMERA_SUPPORT +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +index 41322ab65802..205c33a004fc 100644 +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -3,6 +3,7 @@ + # Makefile for the video capture/playback device drivers. + # + ++obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o + obj-$(CONFIG_VIDEO_CADENCE) += cadence/ + obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o + obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +new file mode 100644 +index 000000000000..692e08ef38c0 +--- /dev/null ++++ b/drivers/media/platform/aspeed-video.c +@@ -0,0 +1,1730 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include <linux/atomic.h> ++#include <linux/bitfield.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/of.h> ++#include <linux/of_irq.h> ++#include <linux/of_reserved_mem.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include <linux/sched.h> ++#include <linux/spinlock.h> ++#include <linux/string.h> ++#include <linux/v4l2-controls.h> ++#include <linux/videodev2.h> ++#include <linux/wait.h> ++#include <linux/workqueue.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-dev.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-dv-timings.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-ioctl.h> ++#include <media/videobuf2-dma-contig.h> ++ ++#define DEVICE_NAME "aspeed-video" ++ ++#define ASPEED_VIDEO_JPEG_NUM_QUALITIES 12 ++#define ASPEED_VIDEO_JPEG_HEADER_SIZE 10 ++#define ASPEED_VIDEO_JPEG_QUANT_SIZE 116 ++#define ASPEED_VIDEO_JPEG_DCT_SIZE 34 ++ ++#define MAX_FRAME_RATE 60 ++#define MAX_HEIGHT 1200 ++#define MAX_WIDTH 1920 ++#define MIN_HEIGHT 480 ++#define MIN_WIDTH 640 ++ ++#define NUM_POLARITY_CHECKS 10 ++#define INVALID_RESOLUTION_RETRIES 2 ++#define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) ++#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(500) ++#define MODE_DETECT_TIMEOUT msecs_to_jiffies(500) ++#define STOP_TIMEOUT msecs_to_jiffies(1000) ++#define DIRECT_FETCH_THRESHOLD 0x0c0000 /* 1024 * 768 */ ++ ++#define VE_MAX_SRC_BUFFER_SIZE 0x8ca000 /* 1920 * 1200, 32bpp */ ++#define VE_JPEG_HEADER_SIZE 0x006000 /* 512 * 12 * 4 */ ++ ++#define VE_PROTECTION_KEY 0x000 ++#define VE_PROTECTION_KEY_UNLOCK 0x1a038aa8 ++ ++#define VE_SEQ_CTRL 0x004 ++#define VE_SEQ_CTRL_TRIG_MODE_DET BIT(0) ++#define VE_SEQ_CTRL_TRIG_CAPTURE BIT(1) ++#define VE_SEQ_CTRL_FORCE_IDLE BIT(2) ++#define VE_SEQ_CTRL_MULT_FRAME BIT(3) ++#define VE_SEQ_CTRL_TRIG_COMP BIT(4) ++#define VE_SEQ_CTRL_AUTO_COMP BIT(5) ++#define VE_SEQ_CTRL_EN_WATCHDOG BIT(7) ++#define VE_SEQ_CTRL_YUV420 BIT(10) ++#define VE_SEQ_CTRL_COMP_FMT GENMASK(11, 10) ++#define VE_SEQ_CTRL_HALT BIT(12) ++#define VE_SEQ_CTRL_EN_WATCHDOG_COMP BIT(14) ++#define VE_SEQ_CTRL_TRIG_JPG BIT(15) ++#define VE_SEQ_CTRL_CAP_BUSY BIT(16) ++#define VE_SEQ_CTRL_COMP_BUSY BIT(18) ++ ++#ifdef CONFIG_MACH_ASPEED_G5 ++#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */ ++#else ++#define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */ ++#endif /* CONFIG_MACH_ASPEED_G5 */ ++ ++#define VE_CTRL 0x008 ++#define VE_CTRL_HSYNC_POL BIT(0) ++#define VE_CTRL_VSYNC_POL BIT(1) ++#define VE_CTRL_SOURCE BIT(2) ++#define VE_CTRL_INT_DE BIT(4) ++#define VE_CTRL_DIRECT_FETCH BIT(5) ++#define VE_CTRL_YUV BIT(6) ++#define VE_CTRL_RGB BIT(7) ++#define VE_CTRL_CAPTURE_FMT GENMASK(7, 6) ++#define VE_CTRL_AUTO_OR_CURSOR BIT(8) ++#define VE_CTRL_CLK_INVERSE BIT(11) ++#define VE_CTRL_CLK_DELAY GENMASK(11, 9) ++#define VE_CTRL_INTERLACE BIT(14) ++#define VE_CTRL_HSYNC_POL_CTRL BIT(15) ++#define VE_CTRL_FRC GENMASK(23, 16) ++ ++#define VE_TGS_0 0x00c ++#define VE_TGS_1 0x010 ++#define VE_TGS_FIRST GENMASK(28, 16) ++#define VE_TGS_LAST GENMASK(12, 0) ++ ++#define VE_SCALING_FACTOR 0x014 ++#define VE_SCALING_FILTER0 0x018 ++#define VE_SCALING_FILTER1 0x01c ++#define VE_SCALING_FILTER2 0x020 ++#define VE_SCALING_FILTER3 0x024 ++ ++#define VE_CAP_WINDOW 0x030 ++#define VE_COMP_WINDOW 0x034 ++#define VE_COMP_PROC_OFFSET 0x038 ++#define VE_COMP_OFFSET 0x03c ++#define VE_JPEG_ADDR 0x040 ++#define VE_SRC0_ADDR 0x044 ++#define VE_SRC_SCANLINE_OFFSET 0x048 ++#define VE_SRC1_ADDR 0x04c ++#define VE_COMP_ADDR 0x054 ++ ++#define VE_STREAM_BUF_SIZE 0x058 ++#define VE_STREAM_BUF_SIZE_N_PACKETS GENMASK(5, 3) ++#define VE_STREAM_BUF_SIZE_P_SIZE GENMASK(2, 0) ++ ++#define VE_COMP_CTRL 0x060 ++#define VE_COMP_CTRL_VQ_DCT_ONLY BIT(0) ++#define VE_COMP_CTRL_VQ_4COLOR BIT(1) ++#define VE_COMP_CTRL_QUANTIZE BIT(2) ++#define VE_COMP_CTRL_EN_BQ BIT(4) ++#define VE_COMP_CTRL_EN_CRYPTO BIT(5) ++#define VE_COMP_CTRL_DCT_CHR GENMASK(10, 6) ++#define VE_COMP_CTRL_DCT_LUM GENMASK(15, 11) ++#define VE_COMP_CTRL_EN_HQ BIT(16) ++#define VE_COMP_CTRL_RSVD BIT(19) ++#define VE_COMP_CTRL_ENCODE GENMASK(21, 20) ++#define VE_COMP_CTRL_HQ_DCT_CHR GENMASK(26, 22) ++#define VE_COMP_CTRL_HQ_DCT_LUM GENMASK(31, 27) ++ ++#define VE_OFFSET_COMP_STREAM 0x078 ++ ++#define VE_SRC_LR_EDGE_DET 0x090 ++#define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0) ++#define VE_SRC_LR_EDGE_DET_NO_V BIT(12) ++#define VE_SRC_LR_EDGE_DET_NO_H BIT(13) ++#define VE_SRC_LR_EDGE_DET_NO_DISP BIT(14) ++#define VE_SRC_LR_EDGE_DET_NO_CLK BIT(15) ++#define VE_SRC_LR_EDGE_DET_RT_SHF 16 ++#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF) ++#define VE_SRC_LR_EDGE_DET_INTERLACE BIT(31) ++ ++#define VE_SRC_TB_EDGE_DET 0x094 ++#define VE_SRC_TB_EDGE_DET_TOP GENMASK(12, 0) ++#define VE_SRC_TB_EDGE_DET_BOT_SHF 16 ++#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF) ++ ++#define VE_MODE_DETECT_STATUS 0x098 ++#define VE_MODE_DETECT_H_PIXELS GENMASK(11, 0) ++#define VE_MODE_DETECT_V_LINES_SHF 16 ++#define VE_MODE_DETECT_V_LINES GENMASK(27, VE_MODE_DETECT_V_LINES_SHF) ++#define VE_MODE_DETECT_STATUS_VSYNC BIT(28) ++#define VE_MODE_DETECT_STATUS_HSYNC BIT(29) ++ ++#define VE_SYNC_STATUS 0x09c ++#define VE_SYNC_STATUS_HSYNC GENMASK(11, 0) ++#define VE_SYNC_STATUS_VSYNC_SHF 16 ++#define VE_SYNC_STATUS_VSYNC GENMASK(27, VE_SYNC_STATUS_VSYNC_SHF) ++ ++#define VE_INTERRUPT_CTRL 0x304 ++#define VE_INTERRUPT_STATUS 0x308 ++#define VE_INTERRUPT_MODE_DETECT_WD BIT(0) ++#define VE_INTERRUPT_CAPTURE_COMPLETE BIT(1) ++#define VE_INTERRUPT_COMP_READY BIT(2) ++#define VE_INTERRUPT_COMP_COMPLETE BIT(3) ++#define VE_INTERRUPT_MODE_DETECT BIT(4) ++#define VE_INTERRUPT_FRAME_COMPLETE BIT(5) ++#define VE_INTERRUPT_DECODE_ERR BIT(6) ++#define VE_INTERRUPT_HALT_READY BIT(8) ++#define VE_INTERRUPT_HANG_WD BIT(9) ++#define VE_INTERRUPT_STREAM_DESC BIT(10) ++#define VE_INTERRUPT_VSYNC_DESC BIT(11) ++ ++#define VE_MODE_DETECT 0x30c ++#define VE_MEM_RESTRICT_START 0x310 ++#define VE_MEM_RESTRICT_END 0x314 ++ ++enum { ++ VIDEO_MODE_DETECT_DONE, ++ VIDEO_RES_CHANGE, ++ VIDEO_RES_DETECT, ++ VIDEO_STREAMING, ++ VIDEO_FRAME_INPRG, ++ VIDEO_STOPPED, ++}; ++ ++struct aspeed_video_addr { ++ unsigned int size; ++ dma_addr_t dma; ++ void *virt; ++}; ++ ++struct aspeed_video_buffer { ++ struct vb2_v4l2_buffer vb; ++ struct list_head link; ++}; ++ ++#define to_aspeed_video_buffer(x) \ ++ container_of((x), struct aspeed_video_buffer, vb) ++ ++struct aspeed_video { ++ void __iomem *base; ++ struct clk *eclk; ++ struct clk *vclk; ++ struct reset_control *rst; ++ ++ struct device *dev; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_device v4l2_dev; ++ struct v4l2_pix_format pix_fmt; ++ struct v4l2_bt_timings active_timings; ++ struct v4l2_bt_timings detected_timings; ++ u32 v4l2_input_status; ++ struct vb2_queue queue; ++ struct video_device vdev; ++ struct mutex video_lock; /* v4l2 and videobuf2 lock */ ++ ++ wait_queue_head_t wait; ++ spinlock_t lock; /* buffer list lock */ ++ struct delayed_work res_work; ++ struct list_head buffers; ++ unsigned long flags; ++ unsigned int sequence; ++ ++ unsigned int max_compressed_size; ++ struct aspeed_video_addr srcs[2]; ++ struct aspeed_video_addr jpeg; ++ ++ bool yuv420; ++ unsigned int frame_rate; ++ unsigned int jpeg_quality; ++ ++ unsigned int frame_bottom; ++ unsigned int frame_left; ++ unsigned int frame_right; ++ unsigned int frame_top; ++}; ++ ++#define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev) ++ ++static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = { ++ 0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff, ++ 0x00002d05, 0x00000000, 0x00000000, 0x00dbff00 ++}; ++ ++static const u32 aspeed_video_jpeg_quant[ASPEED_VIDEO_JPEG_QUANT_SIZE] = { ++ 0x081100c0, 0x00000000, 0x00110103, 0x03011102, 0xc4ff0111, 0x00001f00, ++ 0x01010501, 0x01010101, 0x00000000, 0x00000000, 0x04030201, 0x08070605, ++ 0xff0b0a09, 0x10b500c4, 0x03010200, 0x03040203, 0x04040505, 0x7d010000, ++ 0x00030201, 0x12051104, 0x06413121, 0x07615113, 0x32147122, 0x08a19181, ++ 0xc1b14223, 0xf0d15215, 0x72623324, 0x160a0982, 0x1a191817, 0x28272625, ++ 0x35342a29, 0x39383736, 0x4544433a, 0x49484746, 0x5554534a, 0x59585756, ++ 0x6564635a, 0x69686766, 0x7574736a, 0x79787776, 0x8584837a, 0x89888786, ++ 0x9493928a, 0x98979695, 0xa3a29a99, 0xa7a6a5a4, 0xb2aaa9a8, 0xb6b5b4b3, ++ 0xbab9b8b7, 0xc5c4c3c2, 0xc9c8c7c6, 0xd4d3d2ca, 0xd8d7d6d5, 0xe2e1dad9, ++ 0xe6e5e4e3, 0xeae9e8e7, 0xf4f3f2f1, 0xf8f7f6f5, 0xc4fffaf9, 0x00011f00, ++ 0x01010103, 0x01010101, 0x00000101, 0x00000000, 0x04030201, 0x08070605, ++ 0xff0b0a09, 0x11b500c4, 0x02010200, 0x04030404, 0x04040507, 0x77020100, ++ 0x03020100, 0x21050411, 0x41120631, 0x71610751, 0x81322213, 0x91421408, ++ 0x09c1b1a1, 0xf0523323, 0xd1726215, 0x3424160a, 0x17f125e1, 0x261a1918, ++ 0x2a292827, 0x38373635, 0x44433a39, 0x48474645, 0x54534a49, 0x58575655, ++ 0x64635a59, 0x68676665, 0x74736a69, 0x78777675, 0x83827a79, 0x87868584, ++ 0x928a8988, 0x96959493, 0x9a999897, 0xa5a4a3a2, 0xa9a8a7a6, 0xb4b3b2aa, ++ 0xb8b7b6b5, 0xc3c2bab9, 0xc7c6c5c4, 0xd2cac9c8, 0xd6d5d4d3, 0xdad9d8d7, ++ 0xe5e4e3e2, 0xe9e8e7e6, 0xf4f3f2ea, 0xf8f7f6f5, 0xdafffaf9, 0x01030c00, ++ 0x03110200, 0x003f0011 ++}; ++ ++static const u32 aspeed_video_jpeg_dct[ASPEED_VIDEO_JPEG_NUM_QUALITIES] ++ [ASPEED_VIDEO_JPEG_DCT_SIZE] = { ++ { 0x0d140043, 0x0c0f110f, 0x11101114, 0x17141516, 0x1e20321e, ++ 0x3d1e1b1b, 0x32242e2b, 0x4b4c3f48, 0x44463f47, 0x61735a50, ++ 0x566c5550, 0x88644644, 0x7a766c65, 0x4d808280, 0x8c978d60, ++ 0x7e73967d, 0xdbff7b80, 0x1f014300, 0x272d2121, 0x3030582d, ++ 0x697bb958, 0xb8b9b97b, 0xb9b8a6a6, 0xb9b9b9b9, 0xb9b9b9b9, ++ 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, ++ 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xffb9b9b9 }, ++ { 0x0c110043, 0x0a0d0f0d, 0x0f0e0f11, 0x14111213, 0x1a1c2b1a, ++ 0x351a1818, 0x2b1f2826, 0x4142373f, 0x3c3d373e, 0x55644e46, ++ 0x4b5f4a46, 0x77573d3c, 0x6b675f58, 0x43707170, 0x7a847b54, ++ 0x6e64836d, 0xdbff6c70, 0x1b014300, 0x22271d1d, 0x2a2a4c27, ++ 0x5b6ba04c, 0xa0a0a06b, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, ++ 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, ++ 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xffa0a0a0 }, ++ { 0x090e0043, 0x090a0c0a, 0x0c0b0c0e, 0x110e0f10, 0x15172415, ++ 0x2c151313, 0x241a211f, 0x36372e34, 0x31322e33, 0x4653413a, ++ 0x3e4e3d3a, 0x62483231, 0x58564e49, 0x385d5e5d, 0x656d6645, ++ 0x5b536c5a, 0xdbff595d, 0x16014300, 0x1c201818, 0x22223f20, ++ 0x4b58853f, 0x85858558, 0x85858585, 0x85858585, 0x85858585, ++ 0x85858585, 0x85858585, 0x85858585, 0x85858585, 0x85858585, ++ 0x85858585, 0x85858585, 0x85858585, 0xff858585 }, ++ { 0x070b0043, 0x07080a08, 0x0a090a0b, 0x0d0b0c0c, 0x11121c11, ++ 0x23110f0f, 0x1c141a19, 0x2b2b2429, 0x27282428, 0x3842332e, ++ 0x313e302e, 0x4e392827, 0x46443e3a, 0x2c4a4a4a, 0x50565137, ++ 0x48425647, 0xdbff474a, 0x12014300, 0x161a1313, 0x1c1c331a, ++ 0x3d486c33, 0x6c6c6c48, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, ++ 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, ++ 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0xff6c6c6c }, ++ { 0x06090043, 0x05060706, 0x07070709, 0x0a09090a, 0x0d0e160d, ++ 0x1b0d0c0c, 0x16101413, 0x21221c20, 0x1e1f1c20, 0x2b332824, ++ 0x26302624, 0x3d2d1f1e, 0x3735302d, 0x22393a39, 0x3f443f2b, ++ 0x38334338, 0xdbff3739, 0x0d014300, 0x11130e0e, 0x15152613, ++ 0x2d355026, 0x50505035, 0x50505050, 0x50505050, 0x50505050, ++ 0x50505050, 0x50505050, 0x50505050, 0x50505050, 0x50505050, ++ 0x50505050, 0x50505050, 0x50505050, 0xff505050 }, ++ { 0x04060043, 0x03040504, 0x05040506, 0x07060606, 0x09090f09, ++ 0x12090808, 0x0f0a0d0d, 0x16161315, 0x14151315, 0x1d221b18, ++ 0x19201918, 0x281e1514, 0x2423201e, 0x17262726, 0x2a2d2a1c, ++ 0x25222d25, 0xdbff2526, 0x09014300, 0x0b0d0a0a, 0x0e0e1a0d, ++ 0x1f25371a, 0x37373725, 0x37373737, 0x37373737, 0x37373737, ++ 0x37373737, 0x37373737, 0x37373737, 0x37373737, 0x37373737, ++ 0x37373737, 0x37373737, 0x37373737, 0xff373737 }, ++ { 0x02030043, 0x01020202, 0x02020203, 0x03030303, 0x04040704, ++ 0x09040404, 0x07050606, 0x0b0b090a, 0x0a0a090a, 0x0e110d0c, ++ 0x0c100c0c, 0x140f0a0a, 0x1211100f, 0x0b131313, 0x1516150e, ++ 0x12111612, 0xdbff1213, 0x04014300, 0x05060505, 0x07070d06, ++ 0x0f121b0d, 0x1b1b1b12, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, ++ 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, ++ 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0xff1b1b1b }, ++ { 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503, ++ 0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908, ++ 0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09, ++ 0x0c0b0f0c, 0xdbff0c0c, 0x03014300, 0x03040303, 0x04040804, ++ 0x0a0c1208, 0x1212120c, 0x12121212, 0x12121212, 0x12121212, ++ 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, ++ 0x12121212, 0x12121212, 0x12121212, 0xff121212 }, ++ { 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503, ++ 0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908, ++ 0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09, ++ 0x0c0b0f0c, 0xdbff0c0c, 0x02014300, 0x03030202, 0x04040703, ++ 0x080a0f07, 0x0f0f0f0a, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, ++ 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, ++ 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0xff0f0f0f }, ++ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x02020302, ++ 0x04020202, 0x03020303, 0x05050405, 0x05050405, 0x07080606, ++ 0x06080606, 0x0a070505, 0x09080807, 0x05090909, 0x0a0b0a07, ++ 0x09080b09, 0xdbff0909, 0x02014300, 0x02030202, 0x03030503, ++ 0x07080c05, 0x0c0c0c08, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, ++ 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, ++ 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xff0c0c0c }, ++ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010201, ++ 0x03010101, 0x02010202, 0x03030303, 0x03030303, 0x04050404, ++ 0x04050404, 0x06050303, 0x06050505, 0x03060606, 0x07070704, ++ 0x06050706, 0xdbff0606, 0x01014300, 0x01020101, 0x02020402, ++ 0x05060904, 0x09090906, 0x09090909, 0x09090909, 0x09090909, ++ 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, ++ 0x09090909, 0x09090909, 0x09090909, 0xff090909 }, ++ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010101, ++ 0x01010101, 0x01010101, 0x01010101, 0x01010101, 0x02020202, ++ 0x02020202, 0x03020101, 0x03020202, 0x01030303, 0x03030302, ++ 0x03020303, 0xdbff0403, 0x01014300, 0x01010101, 0x01010201, ++ 0x03040602, 0x06060604, 0x06060606, 0x06060606, 0x06060606, ++ 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, ++ 0x06060606, 0x06060606, 0x06060606, 0xff060606 } ++}; ++ ++static const struct v4l2_dv_timings_cap aspeed_video_timings_cap = { ++ .type = V4L2_DV_BT_656_1120, ++ .bt = { ++ .min_width = MIN_WIDTH, ++ .max_width = MAX_WIDTH, ++ .min_height = MIN_HEIGHT, ++ .max_height = MAX_HEIGHT, ++ .min_pixelclock = 6574080, /* 640 x 480 x 24Hz */ ++ .max_pixelclock = 138240000, /* 1920 x 1200 x 60Hz */ ++ .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | ++ V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, ++ .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | ++ V4L2_DV_BT_CAP_REDUCED_BLANKING | ++ V4L2_DV_BT_CAP_CUSTOM, ++ }, ++}; ++ ++static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420) ++{ ++ int i; ++ unsigned int base; ++ ++ for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) { ++ base = 256 * i; /* AST HW requires this header spacing */ ++ memcpy(&table[base], aspeed_video_jpeg_header, ++ sizeof(aspeed_video_jpeg_header)); ++ ++ base += ASPEED_VIDEO_JPEG_HEADER_SIZE; ++ memcpy(&table[base], aspeed_video_jpeg_dct[i], ++ sizeof(aspeed_video_jpeg_dct[i])); ++ ++ base += ASPEED_VIDEO_JPEG_DCT_SIZE; ++ memcpy(&table[base], aspeed_video_jpeg_quant, ++ sizeof(aspeed_video_jpeg_quant)); ++ ++ if (yuv420) ++ table[base + 2] = 0x00220103; ++ } ++} ++ ++static void aspeed_video_update(struct aspeed_video *video, u32 reg, u32 clear, ++ u32 bits) ++{ ++ u32 t = readl(video->base + reg); ++ u32 before = t; ++ ++ t &= ~clear; ++ t |= bits; ++ writel(t, video->base + reg); ++ dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before, ++ readl(video->base + reg)); ++} ++ ++static u32 aspeed_video_read(struct aspeed_video *video, u32 reg) ++{ ++ u32 t = readl(video->base + reg); ++ ++ dev_dbg(video->dev, "read %03x[%08x]\n", reg, t); ++ return t; ++} ++ ++static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val) ++{ ++ writel(val, video->base + reg); ++ dev_dbg(video->dev, "write %03x[%08x]\n", reg, ++ readl(video->base + reg)); ++} ++ ++static int aspeed_video_start_frame(struct aspeed_video *video) ++{ ++ dma_addr_t addr; ++ unsigned long flags; ++ struct aspeed_video_buffer *buf; ++ u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL); ++ ++ if (video->v4l2_input_status) { ++ dev_dbg(video->dev, "No signal; don't start frame\n"); ++ return 0; ++ } ++ ++ if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) || ++ !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) { ++ dev_err(video->dev, "Engine busy; don't start frame\n"); ++ return -EBUSY; ++ } ++ ++ spin_lock_irqsave(&video->lock, flags); ++ buf = list_first_entry_or_null(&video->buffers, ++ struct aspeed_video_buffer, link); ++ if (!buf) { ++ spin_unlock_irqrestore(&video->lock, flags); ++ dev_dbg(video->dev, "No buffers; don't start frame\n"); ++ return -EPROTO; ++ } ++ ++ set_bit(VIDEO_FRAME_INPRG, &video->flags); ++ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); ++ spin_unlock_irqrestore(&video->lock, flags); ++ ++ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); ++ aspeed_video_write(video, VE_COMP_OFFSET, 0); ++ aspeed_video_write(video, VE_COMP_ADDR, addr); ++ ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, ++ VE_INTERRUPT_COMP_COMPLETE | ++ VE_INTERRUPT_CAPTURE_COMPLETE); ++ ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, ++ VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP); ++ ++ return 0; ++} ++ ++static void aspeed_video_enable_mode_detect(struct aspeed_video *video) ++{ ++ /* Enable mode detect interrupts */ ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, ++ VE_INTERRUPT_MODE_DETECT); ++ ++ /* Trigger mode detect */ ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_MODE_DET); ++} ++ ++static void aspeed_video_reset(struct aspeed_video *video) ++{ ++ /* Reset the engine */ ++ reset_control_assert(video->rst); ++ ++ /* Don't usleep here; function may be called in interrupt context */ ++ udelay(100); ++ reset_control_deassert(video->rst); ++} ++ ++static void aspeed_video_off(struct aspeed_video *video) ++{ ++ aspeed_video_reset(video); ++ ++ /* Turn off the relevant clocks */ ++ clk_disable_unprepare(video->vclk); ++ clk_disable_unprepare(video->eclk); ++} ++ ++static void aspeed_video_on(struct aspeed_video *video) ++{ ++ /* Turn on the relevant clocks */ ++ clk_prepare_enable(video->eclk); ++ clk_prepare_enable(video->vclk); ++ ++ aspeed_video_reset(video); ++} ++ ++static void aspeed_video_bufs_done(struct aspeed_video *video, ++ enum vb2_buffer_state state) ++{ ++ unsigned long flags; ++ struct aspeed_video_buffer *buf; ++ ++ spin_lock_irqsave(&video->lock, flags); ++ list_for_each_entry(buf, &video->buffers, link) ++ vb2_buffer_done(&buf->vb.vb2_buf, state); ++ INIT_LIST_HEAD(&video->buffers); ++ spin_unlock_irqrestore(&video->lock, flags); ++} ++ ++static void aspeed_video_irq_res_change(struct aspeed_video *video) ++{ ++ dev_dbg(video->dev, "Resolution changed; resetting\n"); ++ ++ set_bit(VIDEO_RES_CHANGE, &video->flags); ++ clear_bit(VIDEO_FRAME_INPRG, &video->flags); ++ ++ aspeed_video_off(video); ++ aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); ++ ++ schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); ++} ++ ++static irqreturn_t aspeed_video_irq(int irq, void *arg) ++{ ++ struct aspeed_video *video = arg; ++ u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS); ++ ++ /* ++ * Resolution changed or signal was lost; reset the engine and ++ * re-initialize ++ */ ++ if (sts & VE_INTERRUPT_MODE_DETECT_WD) { ++ aspeed_video_irq_res_change(video); ++ return IRQ_HANDLED; ++ } ++ ++ if (sts & VE_INTERRUPT_MODE_DETECT) { ++ if (test_bit(VIDEO_RES_DETECT, &video->flags)) { ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, ++ VE_INTERRUPT_MODE_DETECT, 0); ++ aspeed_video_write(video, VE_INTERRUPT_STATUS, ++ VE_INTERRUPT_MODE_DETECT); ++ ++ set_bit(VIDEO_MODE_DETECT_DONE, &video->flags); ++ wake_up_interruptible_all(&video->wait); ++ } else { ++ /* ++ * Signal acquired while NOT doing resolution ++ * detection; reset the engine and re-initialize ++ */ ++ aspeed_video_irq_res_change(video); ++ return IRQ_HANDLED; ++ } ++ } ++ ++ if ((sts & VE_INTERRUPT_COMP_COMPLETE) && ++ (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) { ++ struct aspeed_video_buffer *buf; ++ u32 frame_size = aspeed_video_read(video, ++ VE_OFFSET_COMP_STREAM); ++ ++ spin_lock(&video->lock); ++ clear_bit(VIDEO_FRAME_INPRG, &video->flags); ++ buf = list_first_entry_or_null(&video->buffers, ++ struct aspeed_video_buffer, ++ link); ++ if (buf) { ++ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size); ++ ++ if (!list_is_last(&buf->link, &video->buffers)) { ++ buf->vb.vb2_buf.timestamp = ktime_get_ns(); ++ buf->vb.sequence = video->sequence++; ++ buf->vb.field = V4L2_FIELD_NONE; ++ vb2_buffer_done(&buf->vb.vb2_buf, ++ VB2_BUF_STATE_DONE); ++ list_del(&buf->link); ++ } ++ } ++ spin_unlock(&video->lock); ++ ++ aspeed_video_update(video, VE_SEQ_CTRL, ++ VE_SEQ_CTRL_TRIG_CAPTURE | ++ VE_SEQ_CTRL_FORCE_IDLE | ++ VE_SEQ_CTRL_TRIG_COMP, 0); ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, ++ VE_INTERRUPT_COMP_COMPLETE | ++ VE_INTERRUPT_CAPTURE_COMPLETE, 0); ++ aspeed_video_write(video, VE_INTERRUPT_STATUS, ++ VE_INTERRUPT_COMP_COMPLETE | ++ VE_INTERRUPT_CAPTURE_COMPLETE); ++ ++ if (test_bit(VIDEO_STREAMING, &video->flags) && buf) ++ aspeed_video_start_frame(video); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) ++{ ++ int i; ++ int hsync_counter = 0; ++ int vsync_counter = 0; ++ u32 sts; ++ ++ for (i = 0; i < NUM_POLARITY_CHECKS; ++i) { ++ sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS); ++ if (sts & VE_MODE_DETECT_STATUS_VSYNC) ++ vsync_counter--; ++ else ++ vsync_counter++; ++ ++ if (sts & VE_MODE_DETECT_STATUS_HSYNC) ++ hsync_counter--; ++ else ++ hsync_counter++; ++ } ++ ++ if (hsync_counter < 0 || vsync_counter < 0) { ++ u32 ctrl; ++ ++ if (hsync_counter < 0) { ++ ctrl = VE_CTRL_HSYNC_POL; ++ video->detected_timings.polarities &= ++ ~V4L2_DV_HSYNC_POS_POL; ++ } else { ++ video->detected_timings.polarities |= ++ V4L2_DV_HSYNC_POS_POL; ++ } ++ ++ if (vsync_counter < 0) { ++ ctrl = VE_CTRL_VSYNC_POL; ++ video->detected_timings.polarities &= ++ ~V4L2_DV_VSYNC_POS_POL; ++ } else { ++ video->detected_timings.polarities |= ++ V4L2_DV_VSYNC_POS_POL; ++ } ++ ++ aspeed_video_update(video, VE_CTRL, 0, ctrl); ++ } ++} ++ ++static bool aspeed_video_alloc_buf(struct aspeed_video *video, ++ struct aspeed_video_addr *addr, ++ unsigned int size) ++{ ++ addr->virt = dma_alloc_coherent(video->dev, size, &addr->dma, ++ GFP_KERNEL); ++ if (!addr->virt) ++ return false; ++ ++ addr->size = size; ++ return true; ++} ++ ++static void aspeed_video_free_buf(struct aspeed_video *video, ++ struct aspeed_video_addr *addr) ++{ ++ dma_free_coherent(video->dev, addr->size, addr->virt, addr->dma); ++ addr->size = 0; ++ addr->dma = 0ULL; ++ addr->virt = NULL; ++} ++ ++/* ++ * Get the minimum HW-supported compression buffer size for the frame size. ++ * Assume worst-case JPEG compression size is 1/8 raw size. This should be ++ * plenty even for maximum quality; any worse and the engine will simply return ++ * incomplete JPEGs. ++ */ ++static void aspeed_video_calc_compressed_size(struct aspeed_video *video, ++ unsigned int frame_size) ++{ ++ int i, j; ++ u32 compression_buffer_size_reg = 0; ++ unsigned int size; ++ const unsigned int num_compression_packets = 4; ++ const unsigned int compression_packet_size = 1024; ++ const unsigned int max_compressed_size = frame_size / 2; /* 4bpp / 8 */ ++ ++ video->max_compressed_size = UINT_MAX; ++ ++ for (i = 0; i < 6; ++i) { ++ for (j = 0; j < 8; ++j) { ++ size = (num_compression_packets << i) * ++ (compression_packet_size << j); ++ if (size < max_compressed_size) ++ continue; ++ ++ if (size < video->max_compressed_size) { ++ compression_buffer_size_reg = (i << 3) | j; ++ video->max_compressed_size = size; ++ } ++ } ++ } ++ ++ aspeed_video_write(video, VE_STREAM_BUF_SIZE, ++ compression_buffer_size_reg); ++ ++ dev_dbg(video->dev, "Max compressed size: %x\n", ++ video->max_compressed_size); ++} ++ ++#define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags) ++ ++static void aspeed_video_get_resolution(struct aspeed_video *video) ++{ ++ bool invalid_resolution = true; ++ int rc; ++ int tries = 0; ++ u32 mds; ++ u32 src_lr_edge; ++ u32 src_tb_edge; ++ u32 sync; ++ struct v4l2_bt_timings *det = &video->detected_timings; ++ ++ det->width = MIN_WIDTH; ++ det->height = MIN_HEIGHT; ++ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; ++ ++ /* ++ * Since we need max buffer size for detection, free the second source ++ * buffer first. ++ */ ++ if (video->srcs[1].size) ++ aspeed_video_free_buf(video, &video->srcs[1]); ++ ++ if (video->srcs[0].size < VE_MAX_SRC_BUFFER_SIZE) { ++ if (video->srcs[0].size) ++ aspeed_video_free_buf(video, &video->srcs[0]); ++ ++ if (!aspeed_video_alloc_buf(video, &video->srcs[0], ++ VE_MAX_SRC_BUFFER_SIZE)) { ++ dev_err(video->dev, ++ "Failed to allocate source buffers\n"); ++ return; ++ } ++ } ++ ++ aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); ++ ++ do { ++ if (tries) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (schedule_timeout(INVALID_RESOLUTION_DELAY)) ++ return; ++ } ++ ++ set_bit(VIDEO_RES_DETECT, &video->flags); ++ aspeed_video_enable_mode_detect(video); ++ ++ rc = wait_event_interruptible_timeout(video->wait, ++ res_check(video), ++ MODE_DETECT_TIMEOUT); ++ if (!rc) { ++ dev_err(video->dev, "Timed out; first mode detect\n"); ++ clear_bit(VIDEO_RES_DETECT, &video->flags); ++ return; ++ } ++ ++ /* Disable mode detect in order to re-trigger */ ++ aspeed_video_update(video, VE_SEQ_CTRL, ++ VE_SEQ_CTRL_TRIG_MODE_DET, 0); ++ ++ aspeed_video_check_and_set_polarity(video); ++ ++ aspeed_video_enable_mode_detect(video); ++ ++ rc = wait_event_interruptible_timeout(video->wait, ++ res_check(video), ++ MODE_DETECT_TIMEOUT); ++ clear_bit(VIDEO_RES_DETECT, &video->flags); ++ if (!rc) { ++ dev_err(video->dev, "Timed out; second mode detect\n"); ++ return; ++ } ++ ++ src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET); ++ src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET); ++ mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS); ++ sync = aspeed_video_read(video, VE_SYNC_STATUS); ++ ++ video->frame_bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >> ++ VE_SRC_TB_EDGE_DET_BOT_SHF; ++ video->frame_top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP; ++ det->vfrontporch = video->frame_top; ++ det->vbackporch = ((mds & VE_MODE_DETECT_V_LINES) >> ++ VE_MODE_DETECT_V_LINES_SHF) - video->frame_bottom; ++ det->vsync = (sync & VE_SYNC_STATUS_VSYNC) >> ++ VE_SYNC_STATUS_VSYNC_SHF; ++ if (video->frame_top > video->frame_bottom) ++ continue; ++ ++ video->frame_right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >> ++ VE_SRC_LR_EDGE_DET_RT_SHF; ++ video->frame_left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT; ++ det->hfrontporch = video->frame_left; ++ det->hbackporch = (mds & VE_MODE_DETECT_H_PIXELS) - ++ video->frame_right; ++ det->hsync = sync & VE_SYNC_STATUS_HSYNC; ++ if (video->frame_left > video->frame_right) ++ continue; ++ ++ invalid_resolution = false; ++ } while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES)); ++ ++ if (invalid_resolution) { ++ dev_err(video->dev, "Invalid resolution detected\n"); ++ return; ++ } ++ ++ det->height = (video->frame_bottom - video->frame_top) + 1; ++ det->width = (video->frame_right - video->frame_left) + 1; ++ video->v4l2_input_status = 0; ++ ++ /* ++ * Enable mode-detect watchdog, resolution-change watchdog and ++ * automatic compression after frame capture. ++ */ ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, ++ VE_INTERRUPT_MODE_DETECT_WD); ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, ++ VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG); ++ ++ dev_dbg(video->dev, "Got resolution: %dx%d\n", det->width, ++ det->height); ++} ++ ++static void aspeed_video_set_resolution(struct aspeed_video *video) ++{ ++ struct v4l2_bt_timings *act = &video->active_timings; ++ unsigned int size = act->width * act->height; ++ ++ aspeed_video_calc_compressed_size(video, size); ++ ++ /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ ++ if (size < DIRECT_FETCH_THRESHOLD) { ++ aspeed_video_write(video, VE_TGS_0, ++ FIELD_PREP(VE_TGS_FIRST, ++ video->frame_left - 1) | ++ FIELD_PREP(VE_TGS_LAST, ++ video->frame_right)); ++ aspeed_video_write(video, VE_TGS_1, ++ FIELD_PREP(VE_TGS_FIRST, video->frame_top) | ++ FIELD_PREP(VE_TGS_LAST, ++ video->frame_bottom + 1)); ++ aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_INT_DE); ++ } else { ++ aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH); ++ } ++ ++ /* Set capture/compression frame sizes */ ++ aspeed_video_write(video, VE_CAP_WINDOW, ++ act->width << 16 | act->height); ++ aspeed_video_write(video, VE_COMP_WINDOW, ++ act->width << 16 | act->height); ++ aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); ++ ++ size *= 4; ++ ++ if (size == video->srcs[0].size / 2) { ++ aspeed_video_write(video, VE_SRC1_ADDR, ++ video->srcs[0].dma + size); ++ } else if (size == video->srcs[0].size) { ++ if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) ++ goto err_mem; ++ ++ aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); ++ } else { ++ aspeed_video_free_buf(video, &video->srcs[0]); ++ ++ if (!aspeed_video_alloc_buf(video, &video->srcs[0], size)) ++ goto err_mem; ++ ++ if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) ++ goto err_mem; ++ ++ aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); ++ aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); ++ } ++ ++ return; ++ ++err_mem: ++ dev_err(video->dev, "Failed to allocate source buffers\n"); ++ ++ if (video->srcs[0].size) ++ aspeed_video_free_buf(video, &video->srcs[0]); ++} ++ ++static void aspeed_video_init_regs(struct aspeed_video *video) ++{ ++ u32 comp_ctrl = VE_COMP_CTRL_RSVD | ++ FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | ++ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); ++ u32 ctrl = VE_CTRL_AUTO_OR_CURSOR; ++ u32 seq_ctrl = VE_SEQ_CTRL_JPEG_MODE; ++ ++ if (video->frame_rate) ++ ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); ++ ++ if (video->yuv420) ++ seq_ctrl |= VE_SEQ_CTRL_YUV420; ++ ++ /* Unlock VE registers */ ++ aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK); ++ ++ /* Disable interrupts */ ++ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); ++ aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); ++ ++ /* Clear the offset */ ++ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); ++ aspeed_video_write(video, VE_COMP_OFFSET, 0); ++ ++ aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma); ++ ++ /* Set control registers */ ++ aspeed_video_write(video, VE_SEQ_CTRL, seq_ctrl); ++ aspeed_video_write(video, VE_CTRL, ctrl); ++ aspeed_video_write(video, VE_COMP_CTRL, comp_ctrl); ++ ++ /* Don't downscale */ ++ aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000); ++ aspeed_video_write(video, VE_SCALING_FILTER0, 0x00200000); ++ aspeed_video_write(video, VE_SCALING_FILTER1, 0x00200000); ++ aspeed_video_write(video, VE_SCALING_FILTER2, 0x00200000); ++ aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000); ++ ++ /* Set mode detection defaults */ ++ aspeed_video_write(video, VE_MODE_DETECT, 0x22666500); ++} ++ ++static void aspeed_video_start(struct aspeed_video *video) ++{ ++ aspeed_video_on(video); ++ ++ aspeed_video_init_regs(video); ++ ++ /* Resolution set to 640x480 if no signal found */ ++ aspeed_video_get_resolution(video); ++ ++ /* Set timings since the device is being opened for the first time */ ++ video->active_timings = video->detected_timings; ++ aspeed_video_set_resolution(video); ++ ++ video->pix_fmt.width = video->active_timings.width; ++ video->pix_fmt.height = video->active_timings.height; ++ video->pix_fmt.sizeimage = video->max_compressed_size; ++} ++ ++static void aspeed_video_stop(struct aspeed_video *video) ++{ ++ set_bit(VIDEO_STOPPED, &video->flags); ++ cancel_delayed_work_sync(&video->res_work); ++ ++ aspeed_video_off(video); ++ ++ if (video->srcs[0].size) ++ aspeed_video_free_buf(video, &video->srcs[0]); ++ ++ if (video->srcs[1].size) ++ aspeed_video_free_buf(video, &video->srcs[1]); ++ ++ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; ++ video->flags = 0; ++} ++ ++static int aspeed_video_querycap(struct file *file, void *fh, ++ struct v4l2_capability *cap) ++{ ++ strscpy(cap->driver, DEVICE_NAME, sizeof(cap->driver)); ++ strscpy(cap->card, "Aspeed Video Engine", sizeof(cap->card)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ++ DEVICE_NAME); ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_format(struct file *file, void *fh, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index) ++ return -EINVAL; ++ ++ f->pixelformat = V4L2_PIX_FMT_JPEG; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_format(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ f->fmt.pix = video->pix_fmt; ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_input(struct file *file, void *fh, ++ struct v4l2_input *inp) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (inp->index) ++ return -EINVAL; ++ ++ strscpy(inp->name, "Host VGA capture", sizeof(inp->name)); ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; ++ inp->status = video->v4l2_input_status; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i) ++{ ++ *i = 0; ++ ++ return 0; ++} ++ ++static int aspeed_video_set_input(struct file *file, void *fh, unsigned int i) ++{ ++ if (i) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ a->parm.capture.readbuffers = 3; ++ a->parm.capture.timeperframe.numerator = 1; ++ if (!video->frame_rate) ++ a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; ++ else ++ a->parm.capture.timeperframe.denominator = video->frame_rate; ++ ++ return 0; ++} ++ ++static int aspeed_video_set_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ unsigned int frame_rate = 0; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ a->parm.capture.readbuffers = 3; ++ ++ if (a->parm.capture.timeperframe.numerator) ++ frame_rate = a->parm.capture.timeperframe.denominator / ++ a->parm.capture.timeperframe.numerator; ++ ++ if (!frame_rate || frame_rate > MAX_FRAME_RATE) { ++ frame_rate = 0; ++ a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; ++ a->parm.capture.timeperframe.numerator = 1; ++ } ++ ++ if (video->frame_rate != frame_rate) { ++ video->frame_rate = frame_rate; ++ aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, ++ FIELD_PREP(VE_CTRL_FRC, frame_rate)); ++ } ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (fsize->index) ++ return -EINVAL; ++ ++ if (fsize->pixel_format != V4L2_PIX_FMT_JPEG) ++ return -EINVAL; ++ ++ fsize->discrete.width = video->pix_fmt.width; ++ fsize->discrete.height = video->pix_fmt.height; ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_frameintervals(struct file *file, void *fh, ++ struct v4l2_frmivalenum *fival) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (fival->index) ++ return -EINVAL; ++ ++ if (fival->width != video->detected_timings.width || ++ fival->height != video->detected_timings.height) ++ return -EINVAL; ++ ++ if (fival->pixel_format != V4L2_PIX_FMT_JPEG) ++ return -EINVAL; ++ ++ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ ++ fival->stepwise.min.denominator = MAX_FRAME_RATE; ++ fival->stepwise.min.numerator = 1; ++ fival->stepwise.max.denominator = 1; ++ fival->stepwise.max.numerator = 1; ++ fival->stepwise.step = fival->stepwise.max; ++ ++ return 0; ++} ++ ++static int aspeed_video_set_dv_timings(struct file *file, void *fh, ++ struct v4l2_dv_timings *timings) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (timings->bt.width == video->active_timings.width && ++ timings->bt.height == video->active_timings.height) ++ return 0; ++ ++ if (vb2_is_busy(&video->queue)) ++ return -EBUSY; ++ ++ video->active_timings = timings->bt; ++ ++ aspeed_video_set_resolution(video); ++ ++ video->pix_fmt.width = timings->bt.width; ++ video->pix_fmt.height = timings->bt.height; ++ video->pix_fmt.sizeimage = video->max_compressed_size; ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_dv_timings(struct file *file, void *fh, ++ struct v4l2_dv_timings *timings) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ timings->bt = video->active_timings; ++ ++ return 0; ++} ++ ++static int aspeed_video_query_dv_timings(struct file *file, void *fh, ++ struct v4l2_dv_timings *timings) ++{ ++ int rc; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ /* ++ * This blocks only if the driver is currently in the process of ++ * detecting a new resolution; in the event of no signal or timeout ++ * this function is woken up. ++ */ ++ if (file->f_flags & O_NONBLOCK) { ++ if (test_bit(VIDEO_RES_CHANGE, &video->flags)) ++ return -EAGAIN; ++ } else { ++ rc = wait_event_interruptible(video->wait, ++ !test_bit(VIDEO_RES_CHANGE, ++ &video->flags)); ++ if (rc) ++ return -EINTR; ++ } ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ timings->bt = video->detected_timings; ++ ++ return video->v4l2_input_status ? -ENOLINK : 0; ++} ++ ++static int aspeed_video_enum_dv_timings(struct file *file, void *fh, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ return v4l2_enum_dv_timings_cap(timings, &aspeed_video_timings_cap, ++ NULL, NULL); ++} ++ ++static int aspeed_video_dv_timings_cap(struct file *file, void *fh, ++ struct v4l2_dv_timings_cap *cap) ++{ ++ *cap = aspeed_video_timings_cap; ++ ++ return 0; ++} ++ ++static int aspeed_video_sub_event(struct v4l2_fh *fh, ++ const struct v4l2_event_subscription *sub) ++{ ++ switch (sub->type) { ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_src_change_event_subscribe(fh, sub); ++ } ++ ++ return v4l2_ctrl_subscribe_event(fh, sub); ++} ++ ++static const struct v4l2_ioctl_ops aspeed_video_ioctl_ops = { ++ .vidioc_querycap = aspeed_video_querycap, ++ ++ .vidioc_enum_fmt_vid_cap = aspeed_video_enum_format, ++ .vidioc_g_fmt_vid_cap = aspeed_video_get_format, ++ .vidioc_s_fmt_vid_cap = aspeed_video_get_format, ++ .vidioc_try_fmt_vid_cap = aspeed_video_get_format, ++ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_expbuf = vb2_ioctl_expbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_enum_input = aspeed_video_enum_input, ++ .vidioc_g_input = aspeed_video_get_input, ++ .vidioc_s_input = aspeed_video_set_input, ++ ++ .vidioc_g_parm = aspeed_video_get_parm, ++ .vidioc_s_parm = aspeed_video_set_parm, ++ .vidioc_enum_framesizes = aspeed_video_enum_framesizes, ++ .vidioc_enum_frameintervals = aspeed_video_enum_frameintervals, ++ ++ .vidioc_s_dv_timings = aspeed_video_set_dv_timings, ++ .vidioc_g_dv_timings = aspeed_video_get_dv_timings, ++ .vidioc_query_dv_timings = aspeed_video_query_dv_timings, ++ .vidioc_enum_dv_timings = aspeed_video_enum_dv_timings, ++ .vidioc_dv_timings_cap = aspeed_video_dv_timings_cap, ++ ++ .vidioc_subscribe_event = aspeed_video_sub_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static void aspeed_video_update_jpeg_quality(struct aspeed_video *video) ++{ ++ u32 comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | ++ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); ++ ++ aspeed_video_update(video, VE_COMP_CTRL, ++ VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR, ++ comp_ctrl); ++} ++ ++static void aspeed_video_update_subsampling(struct aspeed_video *video) ++{ ++ if (video->jpeg.virt) ++ aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); ++ ++ if (video->yuv420) ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_YUV420); ++ else ++ aspeed_video_update(video, VE_SEQ_CTRL, VE_SEQ_CTRL_YUV420, 0); ++} ++ ++static int aspeed_video_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct aspeed_video *video = container_of(ctrl->handler, ++ struct aspeed_video, ++ ctrl_handler); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_JPEG_COMPRESSION_QUALITY: ++ video->jpeg_quality = ctrl->val; ++ aspeed_video_update_jpeg_quality(video); ++ break; ++ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: ++ if (ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_420) { ++ video->yuv420 = true; ++ aspeed_video_update_subsampling(video); ++ } else { ++ video->yuv420 = false; ++ aspeed_video_update_subsampling(video); ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops aspeed_video_ctrl_ops = { ++ .s_ctrl = aspeed_video_set_ctrl, ++}; ++ ++static void aspeed_video_resolution_work(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct aspeed_video *video = container_of(dwork, struct aspeed_video, ++ res_work); ++ u32 input_status = video->v4l2_input_status; ++ ++ aspeed_video_on(video); ++ ++ /* Exit early in case no clients remain */ ++ if (test_bit(VIDEO_STOPPED, &video->flags)) ++ goto done; ++ ++ aspeed_video_init_regs(video); ++ ++ aspeed_video_get_resolution(video); ++ ++ if (video->detected_timings.width != video->active_timings.width || ++ video->detected_timings.height != video->active_timings.height || ++ input_status != video->v4l2_input_status) { ++ static const struct v4l2_event ev = { ++ .type = V4L2_EVENT_SOURCE_CHANGE, ++ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, ++ }; ++ ++ v4l2_event_queue(&video->vdev, &ev); ++ } else if (test_bit(VIDEO_STREAMING, &video->flags)) { ++ /* No resolution change so just restart streaming */ ++ aspeed_video_start_frame(video); ++ } ++ ++done: ++ clear_bit(VIDEO_RES_CHANGE, &video->flags); ++ wake_up_interruptible_all(&video->wait); ++} ++ ++static int aspeed_video_open(struct file *file) ++{ ++ int rc; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ mutex_lock(&video->video_lock); ++ ++ rc = v4l2_fh_open(file); ++ if (rc) { ++ mutex_unlock(&video->video_lock); ++ return rc; ++ } ++ ++ if (v4l2_fh_is_singular_file(file)) ++ aspeed_video_start(video); ++ ++ mutex_unlock(&video->video_lock); ++ ++ return 0; ++} ++ ++static int aspeed_video_release(struct file *file) ++{ ++ int rc; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ mutex_lock(&video->video_lock); ++ ++ if (v4l2_fh_is_singular_file(file)) ++ aspeed_video_stop(video); ++ ++ rc = _vb2_fop_release(file, NULL); ++ ++ mutex_unlock(&video->video_lock); ++ ++ return rc; ++} ++ ++static const struct v4l2_file_operations aspeed_video_v4l2_fops = { ++ .owner = THIS_MODULE, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vb2_fop_mmap, ++ .open = aspeed_video_open, ++ .release = aspeed_video_release, ++}; ++ ++static int aspeed_video_queue_setup(struct vb2_queue *q, ++ unsigned int *num_buffers, ++ unsigned int *num_planes, ++ unsigned int sizes[], ++ struct device *alloc_devs[]) ++{ ++ struct aspeed_video *video = vb2_get_drv_priv(q); ++ ++ if (*num_planes) { ++ if (sizes[0] < video->max_compressed_size) ++ return -EINVAL; ++ ++ return 0; ++ } ++ ++ *num_planes = 1; ++ sizes[0] = video->max_compressed_size; ++ ++ return 0; ++} ++ ++static int aspeed_video_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue); ++ ++ if (vb2_plane_size(vb, 0) < video->max_compressed_size) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int aspeed_video_start_streaming(struct vb2_queue *q, ++ unsigned int count) ++{ ++ int rc; ++ struct aspeed_video *video = vb2_get_drv_priv(q); ++ ++ video->sequence = 0; ++ ++ rc = aspeed_video_start_frame(video); ++ if (rc) { ++ aspeed_video_bufs_done(video, VB2_BUF_STATE_QUEUED); ++ return rc; ++ } ++ ++ set_bit(VIDEO_STREAMING, &video->flags); ++ return 0; ++} ++ ++static void aspeed_video_stop_streaming(struct vb2_queue *q) ++{ ++ int rc; ++ struct aspeed_video *video = vb2_get_drv_priv(q); ++ ++ clear_bit(VIDEO_STREAMING, &video->flags); ++ ++ rc = wait_event_timeout(video->wait, ++ !test_bit(VIDEO_FRAME_INPRG, &video->flags), ++ STOP_TIMEOUT); ++ if (!rc) { ++ dev_err(video->dev, "Timed out when stopping streaming\n"); ++ ++ /* ++ * Need to force stop any DMA and try and get HW into a good ++ * state for future calls to start streaming again. ++ */ ++ aspeed_video_reset(video); ++ aspeed_video_init_regs(video); ++ ++ aspeed_video_get_resolution(video); ++ } ++ ++ aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); ++} ++ ++static void aspeed_video_buf_queue(struct vb2_buffer *vb) ++{ ++ bool empty; ++ struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); ++ struct aspeed_video_buffer *avb = to_aspeed_video_buffer(vbuf); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&video->lock, flags); ++ empty = list_empty(&video->buffers); ++ list_add_tail(&avb->link, &video->buffers); ++ spin_unlock_irqrestore(&video->lock, flags); ++ ++ if (test_bit(VIDEO_STREAMING, &video->flags) && ++ !test_bit(VIDEO_FRAME_INPRG, &video->flags) && empty) ++ aspeed_video_start_frame(video); ++} ++ ++static const struct vb2_ops aspeed_video_vb2_ops = { ++ .queue_setup = aspeed_video_queue_setup, ++ .wait_prepare = vb2_ops_wait_prepare, ++ .wait_finish = vb2_ops_wait_finish, ++ .buf_prepare = aspeed_video_buf_prepare, ++ .start_streaming = aspeed_video_start_streaming, ++ .stop_streaming = aspeed_video_stop_streaming, ++ .buf_queue = aspeed_video_buf_queue, ++}; ++ ++static int aspeed_video_setup_video(struct aspeed_video *video) ++{ ++ const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) | ++ BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420)); ++ struct v4l2_device *v4l2_dev = &video->v4l2_dev; ++ struct vb2_queue *vbq = &video->queue; ++ struct video_device *vdev = &video->vdev; ++ int rc; ++ ++ video->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; ++ video->pix_fmt.field = V4L2_FIELD_NONE; ++ video->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB; ++ video->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE; ++ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; ++ ++ rc = v4l2_device_register(video->dev, v4l2_dev); ++ if (rc) { ++ dev_err(video->dev, "Failed to register v4l2 device\n"); ++ return rc; ++ } ++ ++ v4l2_ctrl_handler_init(&video->ctrl_handler, 2); ++ v4l2_ctrl_new_std(&video->ctrl_handler, &aspeed_video_ctrl_ops, ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, ++ ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1, 1, 0); ++ v4l2_ctrl_new_std_menu(&video->ctrl_handler, &aspeed_video_ctrl_ops, ++ V4L2_CID_JPEG_CHROMA_SUBSAMPLING, ++ V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, ++ V4L2_JPEG_CHROMA_SUBSAMPLING_444); ++ ++ if (video->ctrl_handler.error) { ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ ++ dev_err(video->dev, "Failed to init controls: %d\n", ++ video->ctrl_handler.error); ++ return rc; ++ } ++ ++ v4l2_dev->ctrl_handler = &video->ctrl_handler; ++ ++ vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; ++ vbq->dev = v4l2_dev->dev; ++ vbq->lock = &video->video_lock; ++ vbq->ops = &aspeed_video_vb2_ops; ++ vbq->mem_ops = &vb2_dma_contig_memops; ++ vbq->drv_priv = video; ++ vbq->buf_struct_size = sizeof(struct aspeed_video_buffer); ++ vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ vbq->min_buffers_needed = 3; ++ ++ rc = vb2_queue_init(vbq); ++ if (rc) { ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ ++ dev_err(video->dev, "Failed to init vb2 queue\n"); ++ return rc; ++ } ++ ++ vdev->queue = vbq; ++ vdev->fops = &aspeed_video_v4l2_fops; ++ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING; ++ vdev->v4l2_dev = v4l2_dev; ++ strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name)); ++ vdev->vfl_type = VFL_TYPE_GRABBER; ++ vdev->vfl_dir = VFL_DIR_RX; ++ vdev->release = video_device_release_empty; ++ vdev->ioctl_ops = &aspeed_video_ioctl_ops; ++ vdev->lock = &video->video_lock; ++ ++ video_set_drvdata(vdev, video); ++ rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0); ++ if (rc) { ++ vb2_queue_release(vbq); ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ ++ dev_err(video->dev, "Failed to register video device\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int aspeed_video_init(struct aspeed_video *video) ++{ ++ int irq; ++ int rc; ++ struct device *dev = video->dev; ++ ++ irq = irq_of_parse_and_map(dev->of_node, 0); ++ if (!irq) { ++ dev_err(dev, "Unable to find IRQ\n"); ++ return -ENODEV; ++ } ++ ++ rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED, ++ DEVICE_NAME, video); ++ if (rc < 0) { ++ dev_err(dev, "Unable to request IRQ %d\n", irq); ++ return rc; ++ } ++ ++ video->eclk = devm_clk_get(dev, "eclk"); ++ if (IS_ERR(video->eclk)) { ++ dev_err(dev, "Unable to get ECLK\n"); ++ return PTR_ERR(video->eclk); ++ } ++ ++ video->vclk = devm_clk_get(dev, "vclk"); ++ if (IS_ERR(video->vclk)) { ++ dev_err(dev, "Unable to get VCLK\n"); ++ return PTR_ERR(video->vclk); ++ } ++ ++ video->rst = devm_reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(video->rst)) { ++ dev_err(dev, "Unable to get VE reset\n"); ++ return PTR_ERR(video->rst); ++ } ++ ++ rc = of_reserved_mem_device_init(dev); ++ if (rc) { ++ dev_err(dev, "Unable to reserve memory\n"); ++ return rc; ++ } ++ ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(dev, "Failed to set DMA mask\n"); ++ of_reserved_mem_device_release(dev); ++ return rc; ++ } ++ ++ if (!aspeed_video_alloc_buf(video, &video->jpeg, ++ VE_JPEG_HEADER_SIZE)) { ++ dev_err(dev, "Failed to allocate DMA for JPEG header\n"); ++ of_reserved_mem_device_release(dev); ++ return rc; ++ } ++ ++ aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); ++ ++ return 0; ++} ++ ++static int aspeed_video_probe(struct platform_device *pdev) ++{ ++ int rc; ++ struct resource *res; ++ struct aspeed_video *video = kzalloc(sizeof(*video), GFP_KERNEL); ++ ++ if (!video) ++ return -ENOMEM; ++ ++ video->frame_rate = 30; ++ video->dev = &pdev->dev; ++ spin_lock_init(&video->lock); ++ mutex_init(&video->video_lock); ++ init_waitqueue_head(&video->wait); ++ INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); ++ INIT_LIST_HEAD(&video->buffers); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ video->base = devm_ioremap_resource(video->dev, res); ++ ++ if (IS_ERR(video->base)) ++ return PTR_ERR(video->base); ++ ++ rc = aspeed_video_init(video); ++ if (rc) ++ return rc; ++ ++ rc = aspeed_video_setup_video(video); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++static int aspeed_video_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); ++ struct aspeed_video *video = to_aspeed_video(v4l2_dev); ++ ++ video_unregister_device(&video->vdev); ++ ++ vb2_queue_release(&video->queue); ++ ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ ++ v4l2_device_unregister(v4l2_dev); ++ ++ dma_free_coherent(video->dev, VE_JPEG_HEADER_SIZE, video->jpeg.virt, ++ video->jpeg.dma); ++ ++ of_reserved_mem_device_release(dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_video_of_match[] = { ++ { .compatible = "aspeed,ast2400-video-engine" }, ++ { .compatible = "aspeed,ast2500-video-engine" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aspeed_video_of_match); ++ ++static struct platform_driver aspeed_video_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_video_of_match, ++ }, ++ .probe = aspeed_video_probe, ++ .remove = aspeed_video_remove, ++}; ++ ++module_platform_driver(aspeed_video_driver); ++ ++MODULE_DESCRIPTION("ASPEED Video Engine Driver"); ++MODULE_AUTHOR("Eddie James"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h +index 335879505a72..0b0f3a0ebe9b 100644 +--- a/include/dt-bindings/clock/aspeed-clock.h ++++ b/include/dt-bindings/clock/aspeed-clock.h +@@ -52,5 +52,6 @@ + #define ASPEED_RESET_I2C 7 + #define ASPEED_RESET_AHB 8 + #define ASPEED_RESET_CRT1 9 ++#define ASPEED_RESET_VIDEO 10 + + #endif +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch new file mode 100644 index 000000000..7400e2848 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch @@ -0,0 +1,702 @@ +From a771e5448ed259f768434d498daf8d8b292713de Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 11 Feb 2019 17:02:35 -0800 +Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP + +This commit adds Aspeed PWM driver which uses timer pulse output +feature in Aspeed SoCs. The timer IP is derived from Faraday +Technologies FTTMR010 IP but has some customized register +structure changes only for Aspeed SoCs. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 2 +- + drivers/clocksource/timer-fttmr010.c | 25 ++ + drivers/input/misc/pwm-beeper.c | 8 +- + drivers/pwm/Kconfig | 9 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-fttmr010.c | 465 +++++++++++++++++++++++++++++++++++ + include/clocksource/timer-fttmr010.h | 17 ++ + 7 files changed, 522 insertions(+), 5 deletions(-) + create mode 100644 drivers/pwm/pwm-fttmr010.c + create mode 100644 include/clocksource/timer-fttmr010.h + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 6686a13a5354..ccf2845cd788 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -301,7 +301,7 @@ + + timer: timer@1e782000 { + /* This timer is a Faraday FTTMR010 derivative */ +- compatible = "aspeed,ast2400-timer"; ++ compatible = "aspeed,ast2500-timer"; + reg = <0x1e782000 0x90>; + interrupts = <16 17 18 35 36 37 38 39>; + clocks = <&syscon ASPEED_CLK_APB>; +diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c +index cf93f6419b51..8226ccf5cc2c 100644 +--- a/drivers/clocksource/timer-fttmr010.c ++++ b/drivers/clocksource/timer-fttmr010.c +@@ -20,6 +20,8 @@ + #include <linux/bitops.h> + #include <linux/delay.h> + ++#include <clocksource/timer-fttmr010.h> ++ + /* + * Register definitions for the timers + */ +@@ -77,6 +79,9 @@ + #define TIMER_3_INT_OVERFLOW BIT(8) + #define TIMER_INT_ALL_MASK 0x1ff + ++DEFINE_SPINLOCK(timer_fttmr010_lock); ++EXPORT_SYMBOL(timer_fttmr010_lock); ++ + struct fttmr010 { + void __iomem *base; + unsigned int tick_rate; +@@ -123,8 +128,11 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, + struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; +@@ -147,27 +155,37 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, + cr |= fttmr010->t1_enable_val; + writel(cr, fttmr010->base + TIMER_CR); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + + static int fttmr010_timer_shutdown(struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; + writel(cr, fttmr010->base + TIMER_CR); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + + static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; +@@ -186,6 +204,8 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) + cr |= TIMER_1_INT_MATCH1; + writel(cr, fttmr010->base + TIMER_INTR_MASK); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + +@@ -193,8 +213,11 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); + u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; +@@ -221,6 +244,8 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) + cr |= fttmr010->t1_enable_val; + writel(cr, fttmr010->base + TIMER_CR); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + +diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c +index edca0d737750..a3baa52f187f 100644 +--- a/drivers/input/misc/pwm-beeper.c ++++ b/drivers/input/misc/pwm-beeper.c +@@ -52,7 +52,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) + if (error) + return error; + +- if (!beeper->amplifier_on) { ++ if (beeper->amplifier && !beeper->amplifier_on) { + error = regulator_enable(beeper->amplifier); + if (error) { + pwm_disable(beeper->pwm); +@@ -67,7 +67,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) + + static void pwm_beeper_off(struct pwm_beeper *beeper) + { +- if (beeper->amplifier_on) { ++ if (beeper->amplifier && beeper->amplifier_on) { + regulator_disable(beeper->amplifier); + beeper->amplifier_on = false; + } +@@ -163,9 +163,9 @@ static int pwm_beeper_probe(struct platform_device *pdev) + if (IS_ERR(beeper->amplifier)) { + error = PTR_ERR(beeper->amplifier); + if (error != -EPROBE_DEFER) +- dev_err(dev, "Failed to get 'amp' regulator: %d\n", ++ dev_dbg(dev, "Failed to get 'amp' regulator: %d\n", + error); +- return error; ++ beeper->amplifier = NULL; + } + + INIT_WORK(&beeper->work, pwm_beeper_work); +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index 504d252716f2..9d4642c668c9 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -168,6 +168,15 @@ config PWM_FSL_FTM + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + ++config PWM_FTTMR010 ++ tristate "Faraday Technology FTTMR010 timer PWM support" ++ help ++ Generic PWM framework driver for Faraday Technology FTTMR010 Timer ++ PWM output ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-fttmr010 ++ + config PWM_HIBVT + tristate "HiSilicon BVT PWM support" + depends on ARCH_HISI || COMPILE_TEST +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index 9c676a0dadf5..13b7b20ad5ab 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_CRC) += pwm-crc.o + obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o + obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o + obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o ++obj-$(CONFIG_PWM_FTTMR010) += pwm-fttmr010.o + obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o + obj-$(CONFIG_PWM_IMG) += pwm-img.o + obj-$(CONFIG_PWM_IMX) += pwm-imx.o +diff --git a/drivers/pwm/pwm-fttmr010.c b/drivers/pwm/pwm-fttmr010.c +new file mode 100644 +index 000000000000..459ace3eba6a +--- /dev/null ++++ b/drivers/pwm/pwm-fttmr010.c +@@ -0,0 +1,465 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2019 Intel Corporation ++ ++#include <linux/clk.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pwm.h> ++ ++/* For timer_fttmr010_lock */ ++#include <clocksource/timer-fttmr010.h> ++ ++#define TIMER_CR 0x30 ++ ++#define TIMER5_ASPEED_COUNT 0x50 ++#define TIMER5_ASPEED_LOAD 0x54 ++#define TIMER5_ASPEED_MATCH1 0x58 ++#define TIMER5_ASPEED_MATCH2 0x5c ++#define TIMER6_ASPEED_COUNT 0x60 ++#define TIMER6_ASPEED_LOAD 0x64 ++#define TIMER6_ASPEED_MATCH1 0x68 ++#define TIMER6_ASPEED_MATCH2 0x6c ++#define TIMER7_ASPEED_COUNT 0x70 ++#define TIMER7_ASPEED_LOAD 0x74 ++#define TIMER7_ASPEED_MATCH1 0x78 ++#define TIMER7_ASPEED_MATCH2 0x7c ++#define TIMER8_ASPEED_COUNT 0x80 ++#define TIMER8_ASPEED_LOAD 0x84 ++#define TIMER8_ASPEED_MATCH1 0x88 ++#define TIMER8_ASPEED_MATCH2 0x8c ++ ++#define TIMER_5_CR_ASPEED_ENABLE BIT(16) ++#define TIMER_5_CR_ASPEED_CLOCK BIT(17) ++#define TIMER_5_CR_ASPEED_INT BIT(18) ++#define TIMER_5_CR_ASPEED_PULSE_OUT BIT(19) ++#define TIMER_6_CR_ASPEED_ENABLE BIT(20) ++#define TIMER_6_CR_ASPEED_CLOCK BIT(21) ++#define TIMER_6_CR_ASPEED_INT BIT(22) ++#define TIMER_6_CR_ASPEED_PULSE_OUT BIT(23) ++#define TIMER_7_CR_ASPEED_ENABLE BIT(24) ++#define TIMER_7_CR_ASPEED_CLOCK BIT(25) ++#define TIMER_7_CR_ASPEED_INT BIT(26) ++#define TIMER_7_CR_ASPEED_PULSE_OUT BIT(27) ++#define TIMER_8_CR_ASPEED_ENABLE BIT(28) ++#define TIMER_8_CR_ASPEED_CLOCK BIT(29) ++#define TIMER_8_CR_ASPEED_INT BIT(30) ++#define TIMER_8_CR_ASPEED_PULSE_OUT BIT(31) ++ ++/** ++ * struct pwm_fttmr010_variant - variant data depends on SoC ++ * @bits: timer counter resolution ++ * @chan_min: lowest timer channel which has pwm pulse output ++ * @chan_max: highest timer channel which has pwm pulse output ++ * @output_mask: pwm pulse output mask which is defined in device tree ++ */ ++struct pwm_fttmr010_variant { ++ u8 bits; ++ u8 chan_min; ++ u8 chan_max; ++ u8 output_mask; ++}; ++ ++/** ++ * struct pwm_fttmr010_chan - private data of FTTMR010 PWM channel ++ * @period_ns: current period in nanoseconds programmed to the hardware ++ * @duty_ns: current duty time in nanoseconds programmed to the hardware ++ */ ++struct pwm_fttmr010_chan { ++ u32 period_ns; ++ u32 duty_ns; ++}; ++ ++/** ++ * struct pwm_fttmr010 - private data of FTTMR010 PWM ++ * @chip: generic PWM chip ++ * @variant: local copy of hardware variant data ++ * @disabled_mask: disabled status for all channels - one bit per channel ++ * @base: base address of mapped PWM registers ++ * @clk: clock used to drive the timers ++ */ ++struct pwm_fttmr010 { ++ struct pwm_chip chip; ++ struct pwm_fttmr010_variant variant; ++ u8 disabled_mask; ++ void __iomem *base; ++ struct clk *clk; ++ u32 clk_tick_ns; ++}; ++ ++#if !defined(CONFIG_FTTMR010_TIMER) ++/* ++ * Timer block is shared between timer-fttmr010 and pwm-fttmr010 drivers ++ * and some registers need access synchronization. If both drivers are ++ * compiled in, the spinlock is defined in the clocksource driver, ++ * otherwise following definition is used. ++ * ++ * Currently we do not need any more complex synchronization method ++ * because all the supported SoCs contain only one instance of the Timer ++ * IP. Once this changes, both drivers will need to be modified to ++ * properly synchronize accesses to particular instances. ++ */ ++static DEFINE_SPINLOCK(timer_fttmr010_lock); ++#endif ++ ++static inline ++struct pwm_fttmr010 *to_pwm_fttmr010(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct pwm_fttmr010, chip); ++} ++ ++static int pwm_fttmr010_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ struct pwm_fttmr010_chan *chan; ++ ++ if (!(priv->variant.output_mask & BIT(pwm->hwpwm))) { ++ dev_warn(chip->dev, ++ "tried to request PWM channel %d without output\n", ++ pwm->hwpwm); ++ return -EINVAL; ++ } ++ ++ chan = devm_kzalloc(chip->dev, sizeof(*chan), GFP_KERNEL); ++ if (!chan) ++ return -ENOMEM; ++ ++ pwm_set_chip_data(pwm, chan); ++ ++ return 0; ++} ++ ++static void pwm_fttmr010_free(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ devm_kfree(chip->dev, pwm_get_chip_data(pwm)); ++ pwm_set_chip_data(pwm, NULL); ++} ++ ++static int pwm_fttmr010_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ulong flags; ++ u32 cr; ++ ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ ++ cr = readl(priv->base + TIMER_CR); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ cr |= (TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); ++ break; ++ case 6: ++ cr |= (TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); ++ break; ++ case 7: ++ cr |= (TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); ++ break; ++ case 8: ++ cr |= (TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); ++ break; ++ } ++ ++ writel(cr, priv->base + TIMER_CR); ++ ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ ++ priv->disabled_mask &= ~BIT(pwm->hwpwm); ++ ++ return 0; ++} ++ ++static void pwm_fttmr010_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ulong flags; ++ u32 cr; ++ ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ ++ cr = readl(priv->base + TIMER_CR); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ cr &= ~(TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); ++ break; ++ case 6: ++ cr &= ~(TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); ++ break; ++ case 7: ++ cr &= ~(TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); ++ break; ++ case 8: ++ cr &= ~(TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); ++ break; ++ } ++ ++ writel(cr, priv->base + TIMER_CR); ++ ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ ++ priv->disabled_mask |= BIT(pwm->hwpwm); ++} ++ ++static int pwm_fttmr010_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ u32 tload, tmatch, creg_offset, lreg_offset, mreg_offset; ++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ++ /* ++ * We currently avoid using 64bit arithmetic by using the ++ * fact that anything faster than 1Hz is easily representable ++ * by 32bits. ++ */ ++ if (period_ns > NSEC_PER_SEC) ++ return -ERANGE; ++ ++ /* No need to update */ ++ if (chan->period_ns == period_ns || chan->duty_ns == duty_ns) ++ return 0; ++ ++ tload = period_ns / priv->clk_tick_ns; ++ ++ /* Period is too short */ ++ if (tload <= 1) ++ return -ERANGE; ++ ++ tmatch = duty_ns / priv->clk_tick_ns; ++ ++ /* 0% duty is not available */ ++ if (!tmatch) ++ ++tmatch; ++ ++ tmatch = tload - tmatch; ++ ++ /* Decrement to get tick numbers, instead of tick counts */ ++ --tload; ++ --tmatch; ++ ++ if (tload == 0 || tmatch == 0) ++ return -ERANGE; ++ ++ dev_dbg(priv->chip.dev, "clk_tick_ns:%u, tload:%u, tmatch:%u\n", ++ priv->clk_tick_ns, tload, tmatch); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ creg_offset = TIMER5_ASPEED_COUNT; ++ lreg_offset = TIMER5_ASPEED_LOAD; ++ mreg_offset = TIMER5_ASPEED_MATCH1; ++ break; ++ case 6: ++ creg_offset = TIMER6_ASPEED_COUNT; ++ lreg_offset = TIMER6_ASPEED_LOAD; ++ mreg_offset = TIMER6_ASPEED_MATCH1; ++ break; ++ case 7: ++ creg_offset = TIMER7_ASPEED_COUNT; ++ lreg_offset = TIMER7_ASPEED_LOAD; ++ mreg_offset = TIMER7_ASPEED_MATCH1; ++ break; ++ case 8: ++ creg_offset = TIMER8_ASPEED_COUNT; ++ lreg_offset = TIMER8_ASPEED_LOAD; ++ mreg_offset = TIMER8_ASPEED_MATCH1; ++ break; ++ } ++ ++ writel(tload, priv->base + creg_offset); ++ writel(tload, priv->base + lreg_offset); ++ writel(tmatch, priv->base + mreg_offset); ++ ++ chan->period_ns = period_ns; ++ chan->duty_ns = duty_ns; ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_fttmr010_ops = { ++ .request = pwm_fttmr010_request, ++ .free = pwm_fttmr010_free, ++ .enable = pwm_fttmr010_enable, ++ .disable = pwm_fttmr010_disable, ++ .config = pwm_fttmr010_config, ++ .owner = THIS_MODULE, ++}; ++ ++#ifdef CONFIG_OF ++static const struct pwm_fttmr010_variant aspeed_variant = { ++ .bits = 32, ++ .chan_min = 5, ++ .chan_max = 8, ++}; ++ ++static const struct of_device_id pwm_fttmr010_matches[] = { ++ { .compatible = "aspeed,ast2400-timer", .data = &aspeed_variant }, ++ { .compatible = "aspeed,ast2500-timer", .data = &aspeed_variant }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, pwm_fttmr010_matches); ++ ++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) ++{ ++ struct device_node *np = priv->chip.dev->of_node; ++ const struct of_device_id *match; ++ struct property *prop; ++ const __be32 *cur; ++ u32 val; ++ ++ match = of_match_node(pwm_fttmr010_matches, np); ++ if (!match) ++ return -ENODEV; ++ ++ memcpy(&priv->variant, match->data, sizeof(priv->variant)); ++ ++ of_property_for_each_u32(np, "fttmr010,pwm-outputs", prop, cur, val) { ++ if (val < priv->variant.chan_min || ++ val > priv->variant.chan_max) { ++ dev_err(priv->chip.dev, ++ "invalid channel index in fttmr010,pwm-outputs property\n"); ++ continue; ++ } ++ priv->variant.output_mask |= BIT(val); ++ } ++ ++ return 0; ++} ++#else ++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) ++{ ++ return -ENODEV; ++} ++#endif ++ ++static int pwm_fttmr010_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pwm_fttmr010 *priv; ++ struct resource *res; ++ ulong clk_rate; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->chip.dev = &pdev->dev; ++ priv->chip.ops = &pwm_fttmr010_ops; ++ priv->chip.base = -1; ++ ++ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { ++ ret = pwm_fttmr010_parse_dt(priv); ++ if (ret) ++ return ret; ++ ++ priv->chip.of_xlate = of_pwm_xlate_with_flags; ++ priv->chip.of_pwm_n_cells = 3; ++ } else { ++ if (!pdev->dev.platform_data) { ++ dev_err(&pdev->dev, "no platform data specified\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(&priv->variant, pdev->dev.platform_data, ++ sizeof(priv->variant)); ++ } ++ ++ priv->chip.npwm = priv->variant.chan_max + 1; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->clk = devm_clk_get(&pdev->dev, "PCLK"); ++ if (IS_ERR(priv->clk)) { ++ dev_err(dev, "failed to get timer base clk\n"); ++ return PTR_ERR(priv->clk); ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable base clock\n"); ++ return ret; ++ } ++ ++ clk_rate = clk_get_rate(priv->clk); ++ priv->clk_tick_ns = NSEC_PER_SEC / clk_rate; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ ret = pwmchip_add(&priv->chip); ++ if (ret < 0) { ++ dev_err(dev, "failed to register PWM chip\n"); ++ clk_disable_unprepare(priv->clk); ++ return ret; ++ } ++ ++ dev_dbg(dev, "clk at %lu\n", clk_rate); ++ ++ return 0; ++} ++ ++static int pwm_fttmr010_remove(struct platform_device *pdev) ++{ ++ struct pwm_fttmr010 *priv = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = pwmchip_remove(&priv->chip); ++ if (ret < 0) ++ return ret; ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int pwm_fttmr010_resume(struct device *dev) ++{ ++ struct pwm_fttmr010 *priv = dev_get_drvdata(dev); ++ struct pwm_chip *chip = &priv->chip; ++ unsigned int i; ++ ++ for (i = chip->variant.chan_min; i < chip->variant.chan_max; i++) { ++ struct pwm_device *pwm = &chip->pwms[i]; ++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); ++ ++ if (!chan) ++ continue; ++ ++ if (chan->period_ns) { ++ pwm_fttmr010_config(chip, pwm, chan->duty_ns, ++ chan->period_ns); ++ } ++ ++ if (priv->disabled_mask & BIT(i)) ++ pwm_fttmr010_disable(chip, pwm); ++ else ++ pwm_fttmr010_enable(chip, pwm); ++ } ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(pwm_fttmr010_pm_ops, NULL, pwm_fttmr010_resume); ++ ++static struct platform_driver pwm_fttmr010_driver = { ++ .driver = { ++ .name = "fttmr010-timer-pwm", ++ .pm = &pwm_fttmr010_pm_ops, ++ .of_match_table = of_match_ptr(pwm_fttmr010_matches), ++ }, ++ .probe = pwm_fttmr010_probe, ++ .remove = pwm_fttmr010_remove, ++}; ++module_platform_driver(pwm_fttmr010_driver); ++ ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/clocksource/timer-fttmr010.h b/include/clocksource/timer-fttmr010.h +new file mode 100644 +index 000000000000..d8d6a2f14130 +--- /dev/null ++++ b/include/clocksource/timer-fttmr010.h +@@ -0,0 +1,17 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef __CLOCKSOURCE_TIMER_FTTMR010_H ++#define __CLOCKSOURCE_TIMER_FTTMR010_H ++ ++#include <linux/spinlock.h> ++ ++/* ++ * Following declaration must be in an ifdef due to this symbol being static ++ * in timer-fttmr010 driver if the clocksource driver is not compiled in and the ++ * spinlock is not shared between both drivers. ++ */ ++#ifdef CONFIG_FTTMR010_TIMER ++extern spinlock_t timer_fttmr010_lock; ++#endif ++ ++#endif /* __CLOCKSOURCE_TIMER_FTTMR010_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch new file mode 100644 index 000000000..85b2f45a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch @@ -0,0 +1,492 @@ +From 38ba0a960fcd17f7b3480fe3025c261fd60fe979 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 15 Feb 2019 16:05:09 -0800 +Subject: [PATCH] i2c: Add mux hold/unhold msg types + +This commit adds mux hold/unhold message types to support extended +mux control for IPMB and MCTP devices. A hold or an unhold message +can be added at the end of I2C message stream wrapped by +repeated-start, also can be used as a single message independantly. + +This mux hold/unhold message will be delivered throughout all mux +levels in the path. Means that if it goes to multi-level mux path, +all muxes will be held/unheld by this message. + +1. Hold message + struct i2c_msg msg; + uint16_t timeout = 5000; // timeout in ms. 5 secs in this example. + + msg.addr = 0x0; // any value can be used. addr will be ignored in this packet. + msg.flags = I2C_M_HOLD; // set this flag to indicate it's a hold message. + msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer. + msg.buf = (uint8_t *)&timeout; // set timeout value. + +2. Unhold message + struct i2c_msg msg; + uint16_t timeout = 0; // set 0 for an unhold message. + + msg.addr = 0x0; // any value can be used. addr will be ignored in this packet. + msg.flags = I2C_M_HOLD; // set this flag to indicate it's an unhold message. + msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer. + msg.buf = (uint8_t *)&timeout; // set timeout value. + + This unhold message can be delivered to a mux adapter even when + a bus is locked so that any holding state can be unheld + immediately by invoking this unhold message. + +This patch would not be welcomed from upstream so it should be kept +in downstream only. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/i2c-core-base.c | 79 +++++++++++++++++++++++++++---- + drivers/i2c/i2c-core-smbus.c | 17 ++++++- + drivers/i2c/i2c-mux.c | 109 +++++++++++++++++++++++++++++++++++++++---- + include/linux/i2c-mux.h | 3 ++ + include/linux/i2c.h | 25 ++++++++++ + include/uapi/linux/i2c.h | 1 + + 6 files changed, 215 insertions(+), 19 deletions(-) + +diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c +index 9200e349f29e..728b818501b1 100644 +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1211,6 +1211,25 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) + } + EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + ++static void i2c_adapter_hold(struct i2c_adapter *adapter, unsigned long timeout) ++{ ++ mutex_lock(&adapter->hold_lock); ++ mod_timer(&adapter->hold_timer, jiffies + timeout); ++} ++ ++static void i2c_adapter_unhold(struct i2c_adapter *adapter) ++{ ++ del_timer_sync(&adapter->hold_timer); ++ mutex_unlock(&adapter->hold_lock); ++} ++ ++static void i2c_adapter_hold_timer_callback(struct timer_list *t) ++{ ++ struct i2c_adapter *adapter = from_timer(adapter, t, hold_timer); ++ ++ i2c_adapter_unhold(adapter); ++} ++ + static int i2c_register_adapter(struct i2c_adapter *adap) + { + int res = -EINVAL; +@@ -1292,6 +1311,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap) + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); + mutex_unlock(&core_lock); + ++ mutex_init(&adap->hold_lock); ++ timer_setup(&adap->hold_timer, i2c_adapter_hold_timer_callback, 0); ++ + return 0; + + out_reg: +@@ -1512,6 +1534,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + ++ i2c_adapter_unhold(adap); ++ + /* Clear the device structure in case this adapter is ever going to be + added again */ + memset(&adap->dev, 0, sizeof(adap->dev)); +@@ -1861,7 +1885,9 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, + */ + int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { ++ enum i2c_hold_msg_type hold_msg; + unsigned long orig_jiffies; ++ unsigned long timeout; + int ret, try; + + if (WARN_ON(!msgs || num < 1)) +@@ -1870,6 +1896,25 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) + return -EOPNOTSUPP; + ++ /* Do not deliver a mux hold msg to root bus adapter */ ++ if (!i2c_parent_is_i2c_adapter(adap)) { ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_adapter_hold(adap, timeout); ++ ++ if (--num == 0) ++ return 0; ++ } else if (hold_msg == I2C_HOLD_MSG_RESET) { ++ i2c_adapter_unhold(adap); ++ return 0; ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&adap->hold_lock); ++ } ++ } ++ + /* + * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets + * enabled. This is an efficient way of keeping the for-loop from +@@ -1902,6 +1947,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + trace_i2c_result(adap, num, ret); + } + ++ if (!i2c_parent_is_i2c_adapter(adap) && hold_msg == I2C_HOLD_MSG_NONE) ++ mutex_unlock(&adap->hold_lock); ++ + return ret; + } + EXPORT_SYMBOL(__i2c_transfer); +@@ -1920,6 +1968,7 @@ EXPORT_SYMBOL(__i2c_transfer); + */ + int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { ++ bool do_bus_lock = true; + int ret; + + /* REVISIT the fault reporting model here is weak: +@@ -1949,18 +1998,30 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); + } + #endif +- +- if (in_atomic() || irqs_disabled()) { +- ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); +- if (!ret) +- /* I2C activity is ongoing. */ +- return -EAGAIN; +- } else { +- i2c_lock_bus(adap, I2C_LOCK_SEGMENT); ++ /* ++ * Do not lock a bus for delivering an unhold msg to a mux ++ * adpater. This is just for a single length unhold msg case. ++ */ ++ if (num == 1 && i2c_parent_is_i2c_adapter(adap) && ++ i2c_check_hold_msg(msgs[0].flags, msgs[0].len, ++ (u16 *)msgs[0].buf) == ++ I2C_HOLD_MSG_RESET) ++ do_bus_lock = false; ++ ++ if (do_bus_lock) { ++ if (in_atomic() || irqs_disabled()) { ++ ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); ++ if (!ret) ++ /* I2C activity is ongoing. */ ++ return -EAGAIN; ++ } else { ++ i2c_lock_bus(adap, I2C_LOCK_SEGMENT); ++ } + } + + ret = __i2c_transfer(adap, msgs, num); +- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); ++ if (do_bus_lock) ++ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + return ret; + } else { +diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c +index 9cd66cabb84f..64c58911bf21 100644 +--- a/drivers/i2c/i2c-core-smbus.c ++++ b/drivers/i2c/i2c-core-smbus.c +@@ -528,12 +528,25 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) + { ++ bool do_bus_lock = true; + s32 res; + +- i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); ++ /* ++ * Do not lock a bus for delivering an unhold msg to a mux adpater. ++ * This is just for a single length unhold msg case. ++ */ ++ if (i2c_parent_is_i2c_adapter(adapter) && ++ i2c_check_hold_msg(flags, ++ protocol == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word) == I2C_HOLD_MSG_RESET) ++ do_bus_lock = false; ++ ++ if (do_bus_lock) ++ i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); + res = __i2c_smbus_xfer(adapter, addr, flags, read_write, + command, protocol, data); +- i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); ++ if (do_bus_lock) ++ i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); + + return res; + } +diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c +index f330690b4125..4d8909a0f90a 100644 +--- a/drivers/i2c/i2c-mux.c ++++ b/drivers/i2c/i2c-mux.c +@@ -26,6 +26,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/slab.h> ++#include <linux/timer.h> + + /* multiplexer per channel data */ + struct i2c_mux_priv { +@@ -35,21 +36,57 @@ struct i2c_mux_priv { + u32 chan_id; + }; + ++static void i2c_mux_hold(struct i2c_mux_core *muxc, unsigned long timeout) ++{ ++ mutex_lock(&muxc->hold_lock); ++ mod_timer(&muxc->hold_timer, jiffies + timeout); ++} ++ ++static void i2c_mux_unhold(struct i2c_mux_core *muxc) ++{ ++ del_timer_sync(&muxc->hold_timer); ++ mutex_unlock(&muxc->hold_lock); ++} ++ ++static void i2c_mux_hold_timer_callback(struct timer_list *t) ++{ ++ struct i2c_mux_core *muxc = from_timer(muxc, t, hold_timer); ++ ++ i2c_mux_unhold(muxc); ++} ++ + static int __i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) + { + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_transfer(parent, msgs, num); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -60,15 +97,32 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_transfer(parent, msgs, num); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -81,16 +135,33 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Select the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(flags, ++ size == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(data->word); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -103,16 +174,33 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Select the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(flags, ++ size == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(data->word); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -263,6 +351,9 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + muxc->deselect = deselect; + muxc->max_adapters = max_adapters; + ++ mutex_init(&muxc->hold_lock); ++ timer_setup(&muxc->hold_timer, i2c_mux_hold_timer_callback, 0); ++ + return muxc; + } + EXPORT_SYMBOL_GPL(i2c_mux_alloc); +@@ -435,6 +526,8 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) + { + char symlink_name[20]; + ++ i2c_mux_unhold(muxc); ++ + while (muxc->num_adapters) { + struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; + struct i2c_mux_priv *priv = adap->algo_data; +diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h +index bd74d5706f3b..bc6f778eaf9d 100644 +--- a/include/linux/i2c-mux.h ++++ b/include/linux/i2c-mux.h +@@ -41,6 +41,9 @@ struct i2c_mux_core { + int (*select)(struct i2c_mux_core *, u32 chan_id); + int (*deselect)(struct i2c_mux_core *, u32 chan_id); + ++ struct mutex hold_lock; /* mutex for channel holding */ ++ struct timer_list hold_timer; ++ + int num_adapters; + int max_adapters; + struct i2c_adapter *adapter[0]; +diff --git a/include/linux/i2c.h b/include/linux/i2c.h +index 65b4eaed1d96..eadde70c0d4a 100644 +--- a/include/linux/i2c.h ++++ b/include/linux/i2c.h +@@ -692,6 +692,13 @@ struct i2c_adapter { + const struct i2c_adapter_quirks *quirks; + + struct irq_domain *host_notify_domain; ++ ++ /* ++ * These will be used by root adpaters only. For muxes, each mux core ++ * has these individually. ++ */ ++ struct mutex hold_lock; /* mutex for bus holding */ ++ struct timer_list hold_timer; + }; + #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) + +@@ -949,4 +956,22 @@ static inline struct i2c_client *i2c_acpi_new_device(struct device *dev, + } + #endif /* CONFIG_ACPI */ + ++enum i2c_hold_msg_type { ++ I2C_HOLD_MSG_NONE, ++ I2C_HOLD_MSG_SET, ++ I2C_HOLD_MSG_RESET ++}; ++ ++static inline enum i2c_hold_msg_type i2c_check_hold_msg(u16 flags, u16 len, u16 *buf) ++{ ++ if (flags & I2C_M_HOLD && len == sizeof(u16)) { ++ if (*buf) ++ return I2C_HOLD_MSG_SET; ++ ++ return I2C_HOLD_MSG_RESET; ++ } ++ ++ return I2C_HOLD_MSG_NONE; ++} ++ + #endif /* _LINUX_I2C_H */ +diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h +index f71a1751cacf..a1db9b17ed36 100644 +--- a/include/uapi/linux/i2c.h ++++ b/include/uapi/linux/i2c.h +@@ -72,6 +72,7 @@ struct i2c_msg { + #define I2C_M_RD 0x0001 /* read data, from slave to master */ + /* I2C_M_RD is guaranteed to be 0x0001! */ + #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ ++#define I2C_M_HOLD 0x0100 /* for holding a mux path */ + #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ + /* makes only sense in kernelspace */ + /* userspace buffers are copied anyway */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch new file mode 100644 index 000000000..9e7757011 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch @@ -0,0 +1,287 @@ +From d34efc982a9206db87da49be3d9b1e20f59be56f Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Thu, 31 Jan 2019 17:47:39 +0800 +Subject: [PATCH] Enable passthrough based gpio character device. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + drivers/gpio/gpio-aspeed.c | 47 ++++++++++++++++++++++- + drivers/gpio/gpiolib.c | 51 +++++++++++++++++++++++-- + drivers/gpio/gpiolib.h | 1 + + include/linux/gpio/consumer.h | 9 +++++ + include/linux/pinctrl/pinconf-generic.h | 2 + + include/uapi/linux/gpio.h | 1 + + 6 files changed, 106 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c +index 2342e154029b..06fd95197684 100644 +--- a/drivers/gpio/gpio-aspeed.c ++++ b/drivers/gpio/gpio-aspeed.c +@@ -17,9 +17,11 @@ + #include <linux/init.h> + #include <linux/io.h> + #include <linux/kernel.h> ++#include <linux/mfd/syscon.h> + #include <linux/module.h> + #include <linux/pinctrl/consumer.h> + #include <linux/platform_device.h> ++#include <linux/regmap.h> + #include <linux/spinlock.h> + #include <linux/string.h> + +@@ -58,6 +60,7 @@ struct aspeed_gpio { + struct gpio_chip chip; + spinlock_t lock; + void __iomem *base; ++ struct regmap *scu; + int irq; + const struct aspeed_gpio_config *config; + +@@ -91,6 +94,13 @@ struct aspeed_gpio_bank { + * and thus can be used to read back what was last written + * reliably. + */ ++#define SCU8C 0x8C /* Multi-function Pin Control #4 */ ++#define PASS_THROUGH1 32 ++#define PASS_THROUGH2 34 ++#define PASS_THROUGH2_MASK 0x2000 ++#define PASS_THROUGH1_MASK 0x1000 ++#define PASS_THROUGH2_ON 0x2000 ++#define PASS_THROUGH1_ON 0x1000 + + static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 }; + +@@ -988,12 +998,38 @@ static int set_debounce(struct gpio_chip *chip, unsigned int offset, + return disable_debounce(chip, offset); + } + ++static int aspeed_gpio_pass_through(struct gpio_chip *chip, unsigned int offset, ++ unsigned long param) ++{ ++ struct aspeed_gpio *gpio = gpiochip_get_data(chip); ++ u32 value; ++ ++ if (!gpio->scu) ++ return -ENOTSUPP; ++ if (param == PIN_CONFIG_PASS_THROUGH_ENABLE){ ++ if (offset == PASS_THROUGH2){ ++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH2_MASK, PASS_THROUGH2_ON); ++ } else if (offset == PASS_THROUGH1){ ++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH1_MASK, PASS_THROUGH1_ON); ++ } ++ } else if (param == PIN_CONFIG_PASS_THROUGH_DISABLE){ ++ if (offset == PASS_THROUGH2){ ++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH2_MASK, ~(PASS_THROUGH2_ON)); ++ } else if (offset == PASS_THROUGH1){ ++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH1_MASK, ~(PASS_THROUGH1_ON)); ++ } ++ } else { ++ return -ENOTSUPP; ++ } ++ ++ return 0; ++} ++ + static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) + { + unsigned long param = pinconf_to_config_param(config); + u32 arg = pinconf_to_config_argument(config); +- + if (param == PIN_CONFIG_INPUT_DEBOUNCE) + return set_debounce(chip, offset, arg); + else if (param == PIN_CONFIG_BIAS_DISABLE || +@@ -1006,6 +1042,9 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + return -ENOTSUPP; + else if (param == PIN_CONFIG_PERSIST_STATE) + return aspeed_gpio_reset_tolerance(chip, offset, arg); ++ else if (param == PIN_CONFIG_PASS_THROUGH_ENABLE || ++ param == PIN_CONFIG_PASS_THROUGH_DISABLE) ++ return aspeed_gpio_pass_through(chip, offset, param); + + return -ENOTSUPP; + } +@@ -1167,7 +1206,11 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) + gpio->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); +- ++ gpio->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu"); ++ if (IS_ERR(gpio->scu)) { ++ dev_err(&pdev->dev, "Failed to find SCU regmap\n"); ++ gpio->scu = NULL; ++ } + spin_lock_init(&gpio->lock); + + gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node); +diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c +index a8e01d99919c..21eeca17583d 100644 +--- a/drivers/gpio/gpiolib.c ++++ b/drivers/gpio/gpiolib.c +@@ -419,6 +419,7 @@ struct linehandle_state { + GPIOHANDLE_REQUEST_OUTPUT | \ + GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_OPEN_DRAIN | \ ++ GPIOHANDLE_REQUEST_PASS_THROUGH | \ + GPIOHANDLE_REQUEST_OPEN_SOURCE) + + static long linehandle_ioctl(struct file *filep, unsigned int cmd, +@@ -519,7 +520,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) + return -EINVAL; + + lflags = handlereq.flags; +- + /* Return an error if an unknown flag is set */ + if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) + return -EINVAL; +@@ -579,6 +579,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); ++ if (lflags & GPIOHANDLE_REQUEST_PASS_THROUGH) ++ set_bit(FLAG_PASS_THROUGH, &desc->flags); + + ret = gpiod_set_transitory(desc, false); + if (ret < 0) +@@ -598,6 +600,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_descs; ++ } else if (lflags & GPIOHANDLE_REQUEST_PASS_THROUGH) { ++ int val = !!handlereq.default_values[i]; ++ ret = gpiod_direction_pass_through(desc, val); ++ if (ret) ++ goto out_free_descs; + } + dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", + offset); +@@ -1010,7 +1017,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + struct gpio_device *gdev = filp->private_data; + struct gpio_chip *chip = gdev->chip; + void __user *ip = (void __user *)arg; +- + /* We fail any subsequent ioctl():s when the chip is gone */ + if (!chip) + return -ENODEV; +@@ -1018,7 +1024,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + /* Fill in the struct and pass to userspace */ + if (cmd == GPIO_GET_CHIPINFO_IOCTL) { + struct gpiochip_info chipinfo; +- + memset(&chipinfo, 0, sizeof(chipinfo)); + + strncpy(chipinfo.name, dev_name(&gdev->dev), +@@ -2643,6 +2648,46 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) + } + EXPORT_SYMBOL_GPL(gpiod_direction_output); + ++/** ++ * gpiod_direction_pass_through - set the GPIO direction to pass-through ++ * @desc: GPIO to set to pass-through ++ * ++ * Set the direction of the passed GPIO to passthrough. ++ * ++ * Return 0 in case of success, else an error code. ++ */ ++int gpiod_direction_pass_through(struct gpio_desc *desc, int val) ++{ ++ struct gpio_chip *gc; ++ ++ VALIDATE_DESC(desc); ++ /* GPIOs used for IRQs shall not be set as pass-through */ ++ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) { ++ gpiod_err(desc, ++ "%s: tried to set a GPIO tied to an IRQ as pass-through\n", ++ __func__); ++ return -EIO; ++ } ++ gc = desc->gdev->chip; ++ val = !!val; ++ if (test_bit(FLAG_PASS_THROUGH, &desc->flags)) { ++ if (val) ++ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), ++ PIN_CONFIG_PASS_THROUGH_ENABLE); ++ else ++ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), ++ PIN_CONFIG_PASS_THROUGH_DISABLE); ++ } else { ++ gpiod_err(desc, ++ "%s: desc->flags is not set to FLAG_PASS_THROUGH\n", ++ __func__); ++ return -EIO; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gpiod_direction_pass_through); ++ + /** + * gpiod_set_debounce - sets @debounce time for a GPIO + * @desc: descriptor of the GPIO for which to set debounce time +diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h +index a7e49fef73d4..b143ee47870a 100644 +--- a/drivers/gpio/gpiolib.h ++++ b/drivers/gpio/gpiolib.h +@@ -210,6 +210,7 @@ struct gpio_desc { + #define FLAG_IS_OUT 1 + #define FLAG_EXPORT 2 /* protected by sysfs_lock */ + #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ ++#define FLAG_PASS_THROUGH 4 /*Gpio is passthrough type*/ + #define FLAG_ACTIVE_LOW 6 /* value has active low */ + #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ + #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ +diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h +index 21ddbe440030..96551839c191 100644 +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -99,6 +99,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); + int gpiod_get_direction(struct gpio_desc *desc); + int gpiod_direction_input(struct gpio_desc *desc); + int gpiod_direction_output(struct gpio_desc *desc, int value); ++int gpiod_direction_pass_through(struct gpio_desc *desc, int val); + int gpiod_direction_output_raw(struct gpio_desc *desc, int value); + + /* Value get/set from non-sleeping context */ +@@ -314,6 +315,14 @@ static inline int gpiod_direction_output(struct gpio_desc *desc, int value) + WARN_ON(1); + return -ENOSYS; + } ++ ++static inline int gpiod_direction_pass_through(struct gpio_desc *desc, int val) ++{ ++ /* GPIO can never have been requested */ ++ WARN_ON(1); ++ return -ENOSYS; ++} ++ + static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value) + { + /* GPIO can never have been requested */ +diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h +index 6c0680641108..59f0cbabb685 100644 +--- a/include/linux/pinctrl/pinconf-generic.h ++++ b/include/linux/pinctrl/pinconf-generic.h +@@ -124,6 +124,8 @@ enum pin_config_param { + PIN_CONFIG_SLEW_RATE, + PIN_CONFIG_SKEW_DELAY, + PIN_CONFIG_PERSIST_STATE, ++ PIN_CONFIG_PASS_THROUGH_ENABLE, ++ PIN_CONFIG_PASS_THROUGH_DISABLE, + PIN_CONFIG_END = 0x7F, + PIN_CONFIG_MAX = 0xFF, + }; +diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h +index 1bf6e6df084b..384ced158412 100644 +--- a/include/uapi/linux/gpio.h ++++ b/include/uapi/linux/gpio.h +@@ -62,6 +62,7 @@ struct gpioline_info { + #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) + #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) + #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) ++#define GPIOHANDLE_REQUEST_PASS_THROUGH (1UL << 5) + + /** + * struct gpiohandle_request - Information about a GPIO handle request +-- +2.19.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch new file mode 100644 index 000000000..901d63645 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch @@ -0,0 +1,105 @@ +From ca4f6555620212ffccd943ea4c58e7ee7b1b0571 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 7 Mar 2019 15:17:40 -0800 +Subject: [PATCH] Add bus-timeout-ms and #retries device tree properties + +BMC uses I2C bus 7 as a PMBus channel to communicate with PSUs, +also ME uses this bus as SMLink to control PSUs so this bus is +managed by multi-masters. In this use case, some arbitration errors +are expected so we need to add retry logic. And PMBus subsystem +uses I2C bus in kernel internally so retry logic should be +supported in kernel level. + +To support the use case, this commit adds 'bus-timeout-ms' and +'#retries' device tree properties to set the bus specific +parameters at kernel boot time without using any additional ioctls +from user space. + +This patch would not be accepted by I2C maintainer in linux +upstream because he doesn't like adding these legacy properties +into device tree, so keep it only in downstream. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + Documentation/devicetree/bindings/i2c/i2c-aspeed.txt | 3 +++ + Documentation/devicetree/bindings/i2c/i2c.txt | 6 ++++++ + drivers/i2c/busses/i2c-aspeed.c | 1 - + drivers/i2c/i2c-core-base.c | 12 ++++++++++-- + 4 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +index 8fbd8633a387..7da7e813b2b0 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +@@ -16,6 +16,9 @@ Optional Properties: + - bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not + specified + - multi-master : states that there is another master active on this bus. ++- bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not ++ specified. ++- #retries : Number of retries for master transfer. + + Example: + +diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt +index 11263982470e..bdead91f82a4 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c.txt +@@ -80,6 +80,12 @@ wants to support one of the below features, it should adapt the bindings below. + Names of map programmable addresses. + It can contain any map needing another address than default one. + ++- bus-timeout-ms ++ Bus timeout in milliseconds. ++ ++- #retries ++ Number of retries for master transfer. ++ + Binding may contain optional "interrupts" property, describing interrupts + used by the device. I2C core will assign "irq" interrupt (or the very first + interrupt if not using interrupt names) as primary interrupt for the slave. +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 506d867b43d9..84237c5d0aca 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -1012,7 +1012,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + spin_lock_init(&bus->lock); + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; +- bus->adap.retries = 0; + bus->adap.algo = &aspeed_i2c_algo; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; +diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c +index 728b818501b1..17cf0fb037ac 100644 +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1232,6 +1232,7 @@ static void i2c_adapter_hold_timer_callback(struct timer_list *t) + + static int i2c_register_adapter(struct i2c_adapter *adap) + { ++ u32 bus_timeout_ms = 0; + int res = -EINVAL; + + /* Can't register until after driver model init */ +@@ -1258,8 +1259,15 @@ static int i2c_register_adapter(struct i2c_adapter *adap) + INIT_LIST_HEAD(&adap->userspace_clients); + + /* Set default timeout to 1 second if not already set */ +- if (adap->timeout == 0) +- adap->timeout = HZ; ++ if (adap->timeout == 0) { ++ device_property_read_u32(&adap->dev, "bus-timeout-ms", ++ &bus_timeout_ms); ++ adap->timeout = bus_timeout_ms ? ++ msecs_to_jiffies(bus_timeout_ms) : HZ; ++ } ++ ++ /* Set retries count if it has the property setting */ ++ device_property_read_u32(&adap->dev, "#retries", &adap->retries); + + /* register soft irqs for Host Notify */ + res = i2c_setup_host_notify_irq_domain(adap); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch new file mode 100644 index 000000000..f04824c0e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch @@ -0,0 +1,140 @@ +From d82aacea62f2cc3f5c4f6654bd8920255edf24fd Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:04:16 -0700 +Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC BT driver + +If LPC BT driver is registered ahead of lpc-ctrl module, LPC BT +block will be enabled without heart beating of LCLK until lpc-ctrl +enables the LCLK. This issue causes improper handling on host +interrupts when the host sends interrupt in that time frame. Then +kernel eventually forcibly disables the interrupt with dumping +stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +BT driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../bindings/ipmi/aspeed,ast2400-ibt-bmc.txt | 3 +++ + arch/arm/boot/dts/aspeed-g4.dtsi | 1 + + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + + drivers/char/ipmi/bt-bmc.c | 24 +++++++++++++++++++++- + 4 files changed, 28 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt +index 028268fd99ee..d13887d60f19 100644 +--- a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt ++++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt +@@ -10,6 +10,8 @@ Required properties: + "aspeed,ast2400-ibt-bmc" + "aspeed,ast2500-ibt-bmc" + - reg: physical address and size of the registers ++- clocks: contains a phandle to the syscon node describing the clocks. ++ There should then be one cell representing the clock to use. + + Optional properties: + +@@ -22,4 +24,5 @@ Example: + compatible = "aspeed,ast2400-ibt-bmc"; + reg = <0x1e789140 0x18>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + }; +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 6e6f50a0fbab..3a7e31f3de07 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -333,6 +333,7 @@ + ibt: ibt@c0 { + compatible = "aspeed,ast2400-ibt-bmc"; + reg = <0xc0 0x18>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <8>; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 4cd4a8258e42..a6720bc952b0 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -468,6 +468,7 @@ + ibt: ibt@c0 { + compatible = "aspeed,ast2500-ibt-bmc"; + reg = <0xc0 0x18>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <8>; + }; + +diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c +index 40b9927c072c..a4ec9d1743d7 100644 +--- a/drivers/char/ipmi/bt-bmc.c ++++ b/drivers/char/ipmi/bt-bmc.c +@@ -5,6 +5,7 @@ + + #include <linux/atomic.h> + #include <linux/bt-bmc.h> ++#include <linux/clk.h> + #include <linux/errno.h> + #include <linux/interrupt.h> + #include <linux/io.h> +@@ -60,6 +61,7 @@ struct bt_bmc { + struct device dev; + struct miscdevice miscdev; + struct regmap *map; ++ struct clk *clk; + int offset; + int irq; + wait_queue_head_t queue; +@@ -467,6 +469,19 @@ static int bt_bmc_probe(struct platform_device *pdev) + mutex_init(&bt_bmc->mutex); + init_waitqueue_head(&bt_bmc->queue); + ++ bt_bmc->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(bt_bmc->clk)) { ++ rc = PTR_ERR(bt_bmc->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(bt_bmc->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR, + bt_bmc->miscdev.name = DEVICE_NAME, + bt_bmc->miscdev.fops = &bt_bmc_fops, +@@ -474,7 +489,7 @@ static int bt_bmc_probe(struct platform_device *pdev) + rc = misc_register(&bt_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register misc device\n"); +- return rc; ++ goto err; + } + + bt_bmc_config_irq(bt_bmc, pdev); +@@ -498,6 +513,11 @@ static int bt_bmc_probe(struct platform_device *pdev) + clr_b_busy(bt_bmc); + + return 0; ++ ++err: ++ clk_disable_unprepare(bt_bmc->clk); ++ ++ return rc; + } + + static int bt_bmc_remove(struct platform_device *pdev) +@@ -507,6 +527,8 @@ static int bt_bmc_remove(struct platform_device *pdev) + misc_deregister(&bt_bmc->miscdev); + if (!bt_bmc->irq) + del_timer_sync(&bt_bmc->poll_timer); ++ clk_disable_unprepare(bt_bmc->clk); ++ + return 0; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch new file mode 100644 index 000000000..0559ef5be --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch @@ -0,0 +1,125 @@ +From 1ebca05f5cb04162e124e59cac701291f23d9091 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:27:48 -0700 +Subject: [PATCH] misc: Add clock control logic into Aspeed LPC SNOOP driver + +If LPC SNOOP driver is registered ahead of lpc-ctrl module, LPC +SNOOP block will be enabled without heart beating of LCLK until +lpc-ctrl enables the LCLK. This issue causes improper handling on +host interrupts when the host sends interrupt in that time frame. +Then kernel eventually forcibly disables the interrupt with +dumping stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +SNOOP driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 1 + + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + + drivers/misc/aspeed-lpc-snoop.c | 30 +++++++++++++++++++++++++++--- + 3 files changed, 29 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 3a7e31f3de07..bedfb77c0158 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -316,6 +316,7 @@ + compatible = "aspeed,ast2400-lpc-snoop"; + reg = <0x0 0x80>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index a6720bc952b0..a26e8b3c09bf 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -451,6 +451,7 @@ + compatible = "aspeed,ast2500-lpc-snoop"; + reg = <0x0 0x80>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/drivers/misc/aspeed-lpc-snoop.c b/drivers/misc/aspeed-lpc-snoop.c +index 2feb4347d67f..39a0471f0b8f 100644 +--- a/drivers/misc/aspeed-lpc-snoop.c ++++ b/drivers/misc/aspeed-lpc-snoop.c +@@ -15,6 +15,7 @@ + */ + + #include <linux/bitops.h> ++#include <linux/clk.h> + #include <linux/interrupt.h> + #include <linux/fs.h> + #include <linux/kfifo.h> +@@ -71,6 +72,7 @@ struct aspeed_lpc_snoop_channel { + struct aspeed_lpc_snoop { + struct regmap *regmap; + int irq; ++ struct clk *clk; + struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; + }; + +@@ -286,22 +288,42 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev) + return -ENODEV; + } + ++ lpc_snoop->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(lpc_snoop->clk)) { ++ rc = PTR_ERR(lpc_snoop->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(lpc_snoop->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) +- return rc; ++ goto err; + + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); + if (rc) +- return rc; ++ goto err; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); +- if (rc) ++ if (rc) { + aspeed_lpc_disable_snoop(lpc_snoop, 0); ++ goto err; ++ } + } + ++ return 0; ++ ++err: ++ clk_disable_unprepare(lpc_snoop->clk); ++ + return rc; + } + +@@ -313,6 +335,8 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev) + aspeed_lpc_disable_snoop(lpc_snoop, 0); + aspeed_lpc_disable_snoop(lpc_snoop, 1); + ++ clk_disable_unprepare(lpc_snoop->clk); ++ + return 0; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch new file mode 100644 index 000000000..d9b6d05f9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch @@ -0,0 +1,235 @@ +From 1326920183042bb91583eb56dabd29ec921f8f65 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:36:34 -0700 +Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC KCS + driver + +If LPC KCS driver is registered ahead of lpc-ctrl module, LPC KCS +block will be enabled without heart beating of LCLK until lpc-ctrl +enables the LCLK. This issue causes improper handling on host +interrupts when the host sends interrupt in that time frame. Then +kernel eventually forcibly disables the interrupt with dumping +stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +KCS driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../devicetree/bindings/ipmi/aspeed-kcs-bmc.txt | 3 ++ + arch/arm/boot/dts/aspeed-g4.dtsi | 35 ++++++++++++++++++++ + arch/arm/boot/dts/aspeed-g5.dtsi | 6 +++- + drivers/char/ipmi/kcs_bmc_aspeed.c | 37 ++++++++++++++++++---- + 4 files changed, 73 insertions(+), 8 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt +index d98a9bf45d6c..3453eb0bf8f2 100644 +--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt ++++ b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt +@@ -9,6 +9,8 @@ Required properties: + "aspeed,ast2400-kcs-bmc" + "aspeed,ast2500-kcs-bmc" + - interrupts : interrupt generated by the controller ++- clocks: contains a phandle to the syscon node describing the clocks. ++ There should then be one cell representing the clock to use. + - kcs_chan : The LPC channel number in the controller + - kcs_addr : The host CPU IO map address + +@@ -19,6 +21,7 @@ Example: + compatible = "aspeed,ast2500-kcs-bmc"; + reg = <0x0 0x80>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + kcs_chan = <3>; + kcs_addr = <0xCA2>; + status = "okay"; +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index bedfb77c0158..a5072ed1f823 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -294,6 +294,33 @@ + lpc_bmc: lpc-bmc@0 { + compatible = "aspeed,ast2400-lpc-bmc"; + reg = <0x0 0x80>; ++ reg-io-width = <4>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x80>; ++ ++ kcs1: kcs1@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ kcs2: kcs2@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ kcs3: kcs3@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <3>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; + }; + + lpc_host: lpc-host@80 { +@@ -305,6 +332,14 @@ + #size-cells = <1>; + ranges = <0x0 0x80 0x1e0>; + ++ kcs4: kcs4@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <4>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ + lpc_ctrl: lpc-ctrl@0 { + compatible = "aspeed,ast2400-lpc-ctrl"; + reg = <0x0 0x80>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index a26e8b3c09bf..6a2f161e7548 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -128,7 +128,7 @@ + }; + + vic: interrupt-controller@1e6c0080 { +- compatible = "aspeed,ast2400-vic"; ++ compatible = "aspeed,ast2500-vic"; + interrupt-controller; + #interrupt-cells = <1>; + valid-sources = <0xfefff7ff 0x0807ffff>; +@@ -408,18 +408,21 @@ + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + kcs2: kcs2@0 { + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + kcs3: kcs3@0 { + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <3>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + }; +@@ -437,6 +440,7 @@ + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <4>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c +index 3c955946e647..bd1912dc5a21 100644 +--- a/drivers/char/ipmi/kcs_bmc_aspeed.c ++++ b/drivers/char/ipmi/kcs_bmc_aspeed.c +@@ -1,11 +1,10 @@ + // SPDX-License-Identifier: GPL-2.0 +-/* +- * Copyright (c) 2015-2018, Intel Corporation. +- */ ++// Copyright (c) 2015-2019, Intel Corporation. + + #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt + + #include <linux/atomic.h> ++#include <linux/clk.h> + #include <linux/errno.h> + #include <linux/interrupt.h> + #include <linux/io.h> +@@ -63,6 +62,7 @@ + + struct aspeed_kcs_bmc { + struct regmap *map; ++ struct clk *clk; + }; + + +@@ -264,36 +264,59 @@ static int aspeed_kcs_probe(struct platform_device *pdev) + return -ENODEV; + } + ++ priv->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ rc = PTR_ERR(priv->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(priv->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1]; + kcs_bmc->io_inputb = aspeed_kcs_inb; + kcs_bmc->io_outputb = aspeed_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + +- aspeed_kcs_set_address(kcs_bmc, addr); +- aspeed_kcs_enable_channel(kcs_bmc, true); + rc = aspeed_kcs_config_irq(kcs_bmc, pdev); + if (rc) +- return rc; ++ goto err; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); +- return rc; ++ goto err; + } + ++ aspeed_kcs_set_address(kcs_bmc, addr); ++ aspeed_kcs_enable_channel(kcs_bmc, true); ++ + pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n", + chan, addr, + kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; ++ ++err: ++ aspeed_kcs_enable_channel(kcs_bmc, false); ++ clk_disable_unprepare(priv->clk); ++ ++ return rc; + } + + static int aspeed_kcs_remove(struct platform_device *pdev) + { + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); ++ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + misc_deregister(&kcs_bmc->miscdev); ++ aspeed_kcs_enable_channel(kcs_bmc, false); ++ clk_disable_unprepare(priv->clk); + + return 0; + } +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch new file mode 100644 index 000000000..220283e24 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch @@ -0,0 +1,166 @@ +From db310b43e5b444a4e2854f3d69d002c2f0d0605c Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:53:24 -0700 +Subject: [PATCH] misc: Add clock control logic into Aspeed LPC MBOX driver + +If LPC MBOX driver is registered ahead of lpc-ctrl module, LPC +MBOX block will be enabled without heart beating of LCLK until +lpc-ctrl enables the LCLK. This issue causes improper handling on +host interrupts when the host sends interrupt in that time frame. +Then kernel eventually forcibly disables the interrupt with dumping +stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +MBOX driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 1 + + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + + drivers/misc/aspeed-lpc-mbox.c | 42 +++++++++++++++++++++++++++++++--------- + 3 files changed, 35 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index a5072ed1f823..729245b74c13 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -389,6 +389,7 @@ + reg = <0x180 0x5c>; + interrupts = <46>; + #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + }; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 6a2f161e7548..df9d63a94264 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -493,6 +493,7 @@ + reg = <0x180 0x5c>; + interrupts = <46>; + #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + }; +diff --git a/drivers/misc/aspeed-lpc-mbox.c b/drivers/misc/aspeed-lpc-mbox.c +index 0933e0553953..f105d27786ac 100644 +--- a/drivers/misc/aspeed-lpc-mbox.c ++++ b/drivers/misc/aspeed-lpc-mbox.c +@@ -7,6 +7,7 @@ + * 2 of the License, or (at your option) any later version. + */ + ++#include <linux/clk.h> + #include <linux/interrupt.h> + #include <linux/mfd/syscon.h> + #include <linux/miscdevice.h> +@@ -37,7 +38,9 @@ + struct aspeed_mbox { + struct miscdevice miscdev; + struct regmap *regmap; ++ struct clk *clk; + unsigned int base; ++ int irq; + wait_queue_head_t queue; + struct mutex mutex; + }; +@@ -237,16 +240,16 @@ static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, + struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +- int rc, irq; ++ int rc; + +- irq = irq_of_parse_and_map(dev->of_node, 0); +- if (!irq) ++ mbox->irq = platform_get_irq(pdev, 0); ++ if (!mbox->irq) + return -ENODEV; + +- rc = devm_request_irq(dev, irq, aspeed_mbox_irq, +- IRQF_SHARED, DEVICE_NAME, mbox); ++ rc = devm_request_irq(dev, mbox->irq, aspeed_mbox_irq, ++ IRQF_SHARED, DEVICE_NAME, mbox); + if (rc < 0) { +- dev_err(dev, "Unable to request IRQ %d\n", irq); ++ dev_err(dev, "Unable to request IRQ %d\n", mbox->irq); + return rc; + } + +@@ -301,6 +304,19 @@ static int aspeed_mbox_probe(struct platform_device *pdev) + mutex_init(&mbox->mutex); + init_waitqueue_head(&mbox->queue); + ++ mbox->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(mbox->clk)) { ++ rc = PTR_ERR(mbox->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(mbox->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + mbox->miscdev.minor = MISC_DYNAMIC_MINOR; + mbox->miscdev.name = DEVICE_NAME; + mbox->miscdev.fops = &aspeed_mbox_fops; +@@ -308,17 +324,24 @@ static int aspeed_mbox_probe(struct platform_device *pdev) + rc = misc_register(&mbox->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); +- return rc; ++ goto err; + } + + rc = aspeed_mbox_config_irq(mbox, pdev); + if (rc) { + dev_err(dev, "Failed to configure IRQ\n"); + misc_deregister(&mbox->miscdev); +- return rc; ++ goto err; + } + ++ dev_info(&pdev->dev, "LPC mbox registered, irq %d\n", mbox->irq); ++ + return 0; ++ ++err: ++ clk_disable_unprepare(mbox->clk); ++ ++ return rc; + } + + static int aspeed_mbox_remove(struct platform_device *pdev) +@@ -326,6 +349,7 @@ static int aspeed_mbox_remove(struct platform_device *pdev) + struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); + + misc_deregister(&mbox->miscdev); ++ clk_disable_unprepare(mbox->clk); + + return 0; + } +@@ -335,6 +359,7 @@ static const struct of_device_id aspeed_mbox_match[] = { + { .compatible = "aspeed,ast2500-mbox" }, + { }, + }; ++MODULE_DEVICE_TABLE(of, aspeed_mbox_match); + + static struct platform_driver aspeed_mbox_driver = { + .driver = { +@@ -347,7 +372,6 @@ static struct platform_driver aspeed_mbox_driver = { + + module_platform_driver(aspeed_mbox_driver); + +-MODULE_DEVICE_TABLE(of, aspeed_mbox_match); + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>"); + MODULE_DESCRIPTION("Aspeed mailbox device driver"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch new file mode 100644 index 000000000..40c26e31b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch @@ -0,0 +1,43 @@ +From 4762687044ec864719ca14d8efa3dccdc3807e70 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:57:08 -0700 +Subject: [PATCH] misc: Block error printing on probe defer case in Aspeed LPC + ctrl + +This commit adds a checking code when it gets -EPROBE_DEFER while +getting a clock resource. In this case it doesn't need to print +out an error message because the probing will be re-visited. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/misc/aspeed-lpc-ctrl.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c +index a024f8042259..c0818c7b0ffb 100644 +--- a/drivers/misc/aspeed-lpc-ctrl.c ++++ b/drivers/misc/aspeed-lpc-ctrl.c +@@ -239,8 +239,10 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) + + lpc_ctrl->clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpc_ctrl->clk)) { +- dev_err(dev, "couldn't get clock\n"); +- return PTR_ERR(lpc_ctrl->clk); ++ rc = PTR_ERR(lpc_ctrl->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; + } + rc = clk_prepare_enable(lpc_ctrl->clk); + if (rc) { +@@ -264,6 +266,7 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) + + err: + clk_disable_unprepare(lpc_ctrl->clk); ++ + return rc; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch new file mode 100644 index 000000000..efbea1be8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch @@ -0,0 +1,40 @@ +From abf63c03805bf7df31133b720e165eab759ea702 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 14 Mar 2019 13:11:49 -0700 +Subject: [PATCH] ARM: dts: aspeed: Set default status of LPC BT as 'disabled' + +LPC BT is not widely used so set its default status as 'disabled'. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 1 + + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 729245b74c13..d4e1e29c6ed2 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -371,6 +371,7 @@ + reg = <0xc0 0x18>; + clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <8>; ++ status = "disabled"; + }; + + sio_regs: regs { +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index df9d63a94264..a3850644b10e 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -475,6 +475,7 @@ + reg = <0xc0 0x18>; + clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <8>; ++ status = "disabled"; + }; + + sio_regs: regs { +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg new file mode 100644 index 000000000..41530dd6e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg @@ -0,0 +1 @@ +CONFIG_BLK_DEV_RAM=y diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend new file mode 100644 index 000000000..1e25709a9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend @@ -0,0 +1,38 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +do_compile_prepend(){ + # device tree compiler flags + export DTC_FLAGS=-@ +} + +SRC_URI += " \ + file://intel.cfg \ + file://0005-arm-dts-aspeed-g5-add-espi.patch \ + file://0007-New-flash-map-for-intel.patch \ + file://0008-Add-ASPEED-SGPIO-driver.patch \ + file://0009-SGPIO-DT-and-pinctrl-fixup.patch \ + file://0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch \ + file://0019-Add-I2C-IPMB-support.patch \ + file://0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch \ + file://0022-Add-AST2500-eSPI-driver.patch \ + file://0026-Add-support-for-new-PECI-commands.patch \ + file://0028-Add-AST2500-JTAG-driver.patch \ + file://0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch \ + file://0030-Add-dump-debug-code-into-I2C-drivers.patch \ + file://0031-Add-high-speed-baud-rate-support-for-UART.patch \ + file://0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch \ + file://0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch \ + file://0035-Implement-a-memory-driver-share-memory.patch \ + file://0036-net-ncsi-backport-ncsi-patches.patch \ + file://0038-media-aspeed-backport-ikvm-patches.patch \ + file://0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch \ + file://0040-i2c-Add-mux-hold-unhold-msg-types.patch \ + file://0041-Enable-passthrough-based-gpio-character-device.patch \ + file://0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch \ + file://0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch \ + file://0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch \ + file://0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch \ + file://0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch \ + file://0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch \ + file://0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch new file mode 100644 index 000000000..15e845ebc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch @@ -0,0 +1,26 @@ +From 61a5796d151337fd537d1aeb3fb072dcdf96abbe Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Sun, 3 Feb 2019 16:08:11 +0800 +Subject: [PATCH] Enable passthrough based gpio character device. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + include/uapi/linux/gpio.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h +index 1bf6e6df..8883ca90 100644 +--- a/include/uapi/linux/gpio.h ++++ b/include/uapi/linux/gpio.h +@@ -62,7 +62,7 @@ struct gpioline_info { + #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) + #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) + #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) +- ++#define GPIOHANDLE_REQUEST_PASS_THROUGH (1UL << 5) + /** + * struct gpiohandle_request - Information about a GPIO handle request + * @lineoffsets: an array desired lines, specified by offset index for the +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend new file mode 100644 index 000000000..703af71df --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://0001-Enable-passthrough-based-gpio-character-device.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network/0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch b/meta-openbmc-mods/meta-common/recipes-network/network/network/0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch new file mode 100644 index 000000000..03460302d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network/0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch @@ -0,0 +1,456 @@ +From 15f9ba436815307c1df7ace505e6f6ee04a4762b Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Thu, 8 Mar 2018 12:18:00 -0800 +Subject: [PATCH 1/3] Patch to keep consistent MAC and IP address inbetween + power cycles + +Currently, your mac will reset upon AC cycle unless you ask systemd use +a MAC provided in your network configuration file. This will write your +randomly generate MAC to the config file upond first boot up. + +Change-Id: Id47d24c62e459cde101add18be2f46c0b010e7fe +Signed-off-by: David Cobbley <david.j.cobbley@linux.intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + ethernet_interface.cpp | 4 +- + ethernet_interface.hpp | 360 +++++++++++++++++++++-------------------- + network_config.cpp | 22 +-- + 3 files changed, 195 insertions(+), 191 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index 3fd7835..fd09b7a 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -200,8 +200,8 @@ InterfaceInfo EthernetInterface::getInterfaceInfo() const + * @return macaddress on success + */ + +-std::string +- EthernetInterface::getMACAddress(const std::string& interfaceName) const ++std::string EthernetInterface::getMACAddress( ++ const std::string& interfaceName) + { + ifreq ifr{}; + char macAddress[mac_address::size]{}; +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index bfe1d54..d62ca34 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -59,185 +59,187 @@ using VlanInterfaceMap = + */ + class EthernetInterface : public Ifaces + { +- public: +- EthernetInterface() = delete; +- EthernetInterface(const EthernetInterface&) = delete; +- EthernetInterface& operator=(const EthernetInterface&) = delete; +- EthernetInterface(EthernetInterface&&) = delete; +- EthernetInterface& operator=(EthernetInterface&&) = delete; +- virtual ~EthernetInterface() = default; +- +- /** @brief Constructor to put object onto bus at a dbus path. +- * @param[in] bus - Bus to attach to. +- * @param[in] objPath - Path to attach at. +- * @param[in] dhcpEnabled - is dhcp enabled(true/false). +- * @param[in] parent - parent object. +- * @param[in] emitSignal - true if the object added signal needs to be +- * send. +- */ +- EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath, +- bool dhcpEnabled, Manager& parent, +- bool emitSignal = true); +- +- /** @brief Function to create ipaddress dbus object. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress- IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway ip address. +- */ +- +- void iP(IP::Protocol addressType, std::string ipaddress, +- uint8_t prefixLength, std::string gateway) override; +- +- /* @brief delete the dbus object of the given ipaddress. +- * @param[in] ipaddress - IP address. +- */ +- void deleteObject(const std::string& ipaddress); +- +- /* @brief delete the vlan dbus object of the given interface. +- * Also deletes the device file and the network file. +- * @param[in] interface - VLAN Interface. +- */ +- void deleteVLANObject(const std::string& interface); +- +- /* @brief creates the dbus object(IPaddres) given in the address list. +- * @param[in] addrs - address list for which dbus objects needs +- * to create. +- */ +- void createIPAddressObjects(); +- +- /* @brief Gets all the ip addresses. +- * @returns the list of ipaddress. +- */ +- const AddressMap& getAddresses() const +- { +- return addrs; +- } +- +- /** Set value of DHCPEnabled */ +- bool dHCPEnabled(bool value) override; +- +- /** @brief sets the MAC address. +- * @param[in] value - MAC address which needs to be set on the system. +- * @returns macAddress of the interface or throws an error. +- */ +- std::string mACAddress(std::string value) override; +- +- /** @brief sets the NTP servers. +- * @param[in] value - vector of NTP servers. +- */ +- ServerList nTPServers(ServerList value) override; +- +- /** @brief sets the DNS/nameservers. +- * @param[in] value - vector of DNS servers. +- */ +- ServerList nameservers(ServerList value) override; +- +- /** @brief create Vlan interface. +- * @param[in] id- VLAN identifier. +- */ +- void createVLAN(VlanId id); +- +- /** @brief load the vlan info from the system +- * and creates the ip address dbus objects. +- * @param[in] vlanID- VLAN identifier. +- */ +- void loadVLAN(VlanId vlanID); +- +- /** @brief write the network conf file with the in-memory objects. +- */ +- void writeConfigurationFile(); +- +- /** @brief delete all dbus objects. +- */ +- void deleteAll(); +- +- using EthernetInterfaceIntf::dHCPEnabled; +- using EthernetInterfaceIntf::interfaceName; +- using MacAddressIntf::mACAddress; +- +- /** @brief Absolute path of the resolv conf file */ +- static constexpr auto resolvConfFile = "/etc/resolv.conf"; +- +- protected: +- /** @brief get the info of the ethernet interface. +- * @return tuple having the link speed,autonegotiation,duplexmode . +- */ +- InterfaceInfo getInterfaceInfo() const; +- +- /* @brief delete the vlan interface from system. +- * @param[in] interface - vlan Interface. +- */ +- void deleteVLANFromSystem(const std::string& interface); +- +- /** @brief get the mac address of the interface. +- * @param[in] interfaceName - Network interface name. +- * @return macaddress on success +- */ +- +- std::string getMACAddress(const std::string& interfaceName) const; +- +- /** @brief construct the ip address dbus object path. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- +- * @return path of the address object. +- */ +- +- std::string generateObjectPath(IP::Protocol addressType, +- const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway) const; +- +- /** @brief generates the id by doing hash of ipaddress, +- * prefixlength and the gateway. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- * @return hash string. +- */ +- +- static std::string generateId(const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway); +- +- /** @brief write the dhcp section **/ +- void writeDHCPSection(std::fstream& stream); +- +- /** @brief get the NTP server list from the network conf +- * +- */ +- ServerList getNTPServersFromConf(); +- +- /** @brief write the DNS entries to resolver file. +- * @param[in] dnsList - DNS server list which needs to be written. +- * @param[in] file - File to write the name server entries to. +- */ +- void writeDNSEntries(const ServerList& dnsList, const std::string& file); +- +- /** @brief get the name server details from the network conf +- * +- */ +- ServerList getNameServerFromConf(); +- +- /** @brief Persistent sdbusplus DBus bus connection. */ +- sdbusplus::bus::bus& bus; +- +- /** @brief Network Manager object. */ +- Manager& manager; +- +- /** @brief Persistent map of IPAddress dbus objects and their names */ +- AddressMap addrs; +- +- /** @brief Persistent map of VLAN interface dbus objects and their names */ +- VlanInterfaceMap vlanInterfaces; +- +- /** @brief Dbus object path */ +- std::string objPath; +- +- friend class TestEthernetInterface; ++ public: ++ EthernetInterface() = delete; ++ EthernetInterface(const EthernetInterface&) = delete; ++ EthernetInterface& operator=(const EthernetInterface&) = delete; ++ EthernetInterface(EthernetInterface&&) = delete; ++ EthernetInterface& operator=(EthernetInterface&&) = delete; ++ virtual ~EthernetInterface() = default; ++ ++ /** @brief Constructor to put object onto bus at a dbus path. ++ * @param[in] bus - Bus to attach to. ++ * @param[in] objPath - Path to attach at. ++ * @param[in] dhcpEnabled - is dhcp enabled(true/false). ++ * @param[in] parent - parent object. ++ * @param[in] emitSignal - true if the object added signal needs to be ++ * send. ++ */ ++ EthernetInterface(sdbusplus::bus::bus& bus, ++ const std::string& objPath, ++ bool dhcpEnabled, ++ Manager& parent, ++ bool emitSignal = true); ++ ++ /** @brief Function to create ipaddress dbus object. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress- IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway ip address. ++ */ ++ ++ void iP(IP::Protocol addressType, ++ std::string ipaddress, ++ uint8_t prefixLength, ++ std::string gateway) override; ++ ++ /* @brief delete the dbus object of the given ipaddress. ++ * @param[in] ipaddress - IP address. ++ */ ++ void deleteObject(const std::string& ipaddress); ++ ++ /* @brief delete the vlan dbus object of the given interface. ++ * Also deletes the device file and the network file. ++ * @param[in] interface - VLAN Interface. ++ */ ++ void deleteVLANObject(const std::string& interface); ++ ++ /* @brief creates the dbus object(IPaddres) given in the address list. ++ * @param[in] addrs - address list for which dbus objects needs ++ * to create. ++ */ ++ void createIPAddressObjects(); ++ ++ /* @brief Gets all the ip addresses. ++ * @returns the list of ipaddress. ++ */ ++ const AddressMap& getAddresses() const { return addrs; } ++ ++ /** Set value of DHCPEnabled */ ++ bool dHCPEnabled(bool value) override; ++ ++ /** @brief sets the MAC address. ++ * @param[in] value - MAC address which needs to be set on the system. ++ * @returns macAddress of the interface or throws an error. ++ */ ++ std::string mACAddress(std::string value) override; ++ ++ /** @brief sets the NTP servers. ++ * @param[in] value - vector of NTP servers. ++ */ ++ ServerList nTPServers(ServerList value) override; ++ ++ /** @brief sets the DNS/nameservers. ++ * @param[in] value - vector of DNS servers. ++ */ ++ ServerList nameservers(ServerList value) override; ++ ++ /** @brief create Vlan interface. ++ * @param[in] id- VLAN identifier. ++ */ ++ void createVLAN(VlanId id); ++ ++ /** @brief load the vlan info from the system ++ * and creates the ip address dbus objects. ++ * @param[in] vlanID- VLAN identifier. ++ */ ++ void loadVLAN(VlanId vlanID); ++ ++ /** @brief write the network conf file with the in-memory objects. ++ */ ++ void writeConfigurationFile(); ++ ++ /** @brief delete all dbus objects. ++ */ ++ void deleteAll(); ++ ++ /** @brief get the mac address of the interface. ++ * @param[in] interfaceName - Network interface name. ++ * @return macaddress on success ++ */ ++ ++ static std::string getMACAddress(const std::string& interfaceName); ++ ++ using EthernetInterfaceIntf::dHCPEnabled; ++ using EthernetInterfaceIntf::interfaceName; ++ using MacAddressIntf::mACAddress; ++ ++ /** @brief Absolute path of the resolv conf file */ ++ static constexpr auto resolvConfFile = "/etc/resolv.conf"; ++ ++ protected: ++ /** @brief get the info of the ethernet interface. ++ * @return tuple having the link speed,autonegotiation,duplexmode . ++ */ ++ InterfaceInfo getInterfaceInfo() const; ++ ++ /* @brief delete the vlan interface from system. ++ * @param[in] interface - vlan Interface. ++ */ ++ void deleteVLANFromSystem(const std::string& interface); ++ ++ /** @brief construct the ip address dbus object path. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ ++ * @return path of the address object. ++ */ ++ ++ std::string generateObjectPath(IP::Protocol addressType, ++ const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway) const; ++ ++ /** @brief generates the id by doing hash of ipaddress, ++ * prefixlength and the gateway. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ * @return hash string. ++ */ ++ ++ static std::string generateId(const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway); ++ ++ /** @brief write the dhcp section **/ ++ void writeDHCPSection(std::fstream& stream);; ++ ++ /** @brief get the NTP server list from the network conf ++ * ++ */ ++ ServerList getNTPServersFromConf(); ++ ++ /** @brief write the DNS entries to resolver file. ++ * @param[in] dnsList - DNS server list which needs to be written. ++ * @param[in] file - File to write the name server entries to. ++ */ ++ void writeDNSEntries(const ServerList& dnsList, ++ const std::string& file); ++ ++ /** @brief get the name server details from the network conf ++ * ++ */ ++ ServerList getNameServerFromConf(); ++ ++ /** @brief Persistent sdbusplus DBus bus connection. */ ++ sdbusplus::bus::bus& bus; ++ ++ /** @brief Network Manager object. */ ++ Manager& manager; ++ ++ /** @brief Persistent map of IPAddress dbus objects and their names */ ++ AddressMap addrs; ++ ++ /** @brief Persistent map of VLAN interface dbus objects and their names */ ++ VlanInterfaceMap vlanInterfaces; ++ ++ /** @brief Dbus object path */ ++ std::string objPath; ++ ++ friend class TestEthernetInterface; + }; + + } // namespace network +diff --git a/network_config.cpp b/network_config.cpp +index e83b16c..8ebad54 100644 +--- a/network_config.cpp ++++ b/network_config.cpp +@@ -1,3 +1,5 @@ ++#include "network_config.hpp" ++#include "ethernet_interface.hpp" + #include "config.h" + + #include "network_config.hpp" +@@ -5,27 +7,27 @@ + #include <fstream> + #include <string> + +-namespace phosphor +-{ +-namespace network +-{ ++namespace phosphor { ++namespace network { + +-namespace bmc +-{ +-void writeDHCPDefault(const std::string& filename, const std::string& interface) ++namespace bmc { ++void writeDHCPDefault(const std::string &filename, const std::string &interface) + { ++ + std::ofstream filestream; + + filestream.open(filename); + filestream << "[Match]\nName=" << interface << +- "\n[Network]\nDHCP=true\n" ++ "\n[Network]\nDHCP=true\n" + #ifdef LINK_LOCAL_AUTOCONFIGURATION + "LinkLocalAddressing=yes\n" + #else + "LinkLocalAddressing=no\n" + #endif +- "IPv6AcceptRA=false\n" +- "[DHCP]\nClientIdentifier=mac\n"; ++ "IPv6AcceptRA=false\n" ++ "[DHCP]\nClientIdentifier=mac\n" ++ "[Link]\nMACAddress=" ++ << EthernetInterface::getMACAddress(interface) << "\n"; + filestream.close(); + } + } // namespace bmc +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network/0002-IPv6-Network-changes-to-configuration-file.patch b/meta-openbmc-mods/meta-common/recipes-network/network/network/0002-IPv6-Network-changes-to-configuration-file.patch new file mode 100644 index 000000000..251f68319 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network/0002-IPv6-Network-changes-to-configuration-file.patch @@ -0,0 +1,210 @@ +From ebb359773b8a5c03a25c3a48c5080bb246c07c71 Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Wed, 6 Jun 2018 11:11:43 -0700 +Subject: [PATCH 2/3] IPv6 Network changes to configuration file + +Allow Additional parameters to be set for IPv6 + +Change-Id: If662f1ce2d265bc525073890c49231bf6f2b8a30 +--- + ethernet_interface.cpp | 109 +++++++++++++++++++++++++++++++++++++++-- + ethernet_interface.hpp | 19 ++++++- + util.cpp | 3 +- + 3 files changed, 124 insertions(+), 7 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index fd09b7a..63f1160 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -46,6 +46,8 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + std::replace(intfName.begin(), intfName.end(), '_', '.'); + interfaceName(intfName); + EthernetInterfaceIntf::dHCPEnabled(dhcpEnabled); ++ EthernetInterfaceIntf::iPAddressEnables(getIPAddressEnablesFromConf()); ++ EthernetInterfaceIntf::iPv6AcceptRA(getIPv6AcceptRAFromConf()); + MacAddressIntf::mACAddress(getMACAddress(intfName)); + EthernetInterfaceIntf::nTPServers(getNTPServersFromConf()); + EthernetInterfaceIntf::nameservers(getNameServerFromConf()); +@@ -322,7 +324,16 @@ std::string EthernetInterface::generateObjectPath( + objectPath /= generateId(ipaddress, prefixLength, gateway); + return objectPath.string(); + } +- ++bool EthernetInterface::iPv6AcceptRA(bool value) ++{ ++ if (value == EthernetInterfaceIntf::iPv6AcceptRA()) ++ { ++ return value; ++ } ++ EthernetInterfaceIntf::iPv6AcceptRA(value); ++ manager.writeToConfigurationFile(); ++ return value; ++} + bool EthernetInterface::dHCPEnabled(bool value) + { + if (value == EthernetInterfaceIntf::dHCPEnabled()) +@@ -433,7 +444,80 @@ void EthernetInterface::createVLAN(VlanId id) + // write the new vlan device entry to the configuration(network) file. + manager.writeToConfigurationFile(); + } ++bool EthernetInterface::getIPv6AcceptRAFromConf() ++{ ++ fs::path confPath = manager.getConfDir(); ++ ++ std::string fileName = systemd::config::networkFilePrefix + ++ interfaceName() + systemd::config::networkFileSuffix; ++ confPath /= fileName; ++ config::ValueList values; ++ config::Parser parser(confPath.string()); ++ auto rc = config::ReturnCode::SUCCESS; ++ std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA"); ++ if (rc != config::ReturnCode::SUCCESS) ++ { ++ log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]", ++ entry("rc=%d", rc)); ++ return false; ++ } ++ if (values[0] == "true") ++ { ++ return true; ++ } ++ ++ return false; ++} ++EthernetInterface::IPAllowed EthernetInterface::getIPAddressEnablesFromConf() ++{ ++ fs::path confPath = manager.getConfDir(); ++ ++ std::string fileName = systemd::config::networkFilePrefix + ++ interfaceName() + systemd::config::networkFileSuffix; ++ confPath /= fileName; ++ config::ValueList values; ++ config::Parser parser(confPath.string()); ++ auto rc = config::ReturnCode::SUCCESS; ++ std::tie(rc, values) = parser.getValues("Network", "DHCP"); ++ if (rc != config::ReturnCode::SUCCESS) ++ { ++ log<level::DEBUG>("Unable to get the value for Network[DHCP]", ++ entry("rc=%d", rc)); ++ return EthernetInterface::IPAllowed::IPv4AndIPv6; ++ } ++ // true, false, ipv4, ipv6 ++ if (values[0] == "ipv6") ++ { ++ return EthernetInterface::IPAllowed::IPv6Only; ++ } ++ else if (values[0] == "ipv4") ++ { ++ return EthernetInterface::IPAllowed::IPv4Only; ++ } ++ else if (values[0] == "off") ++ { ++ // This function should not get called if DHCP == off ++ log<level::DEBUG>("Function not available in static mode"); ++ return EthernetInterface::IPAllowed::IPv4AndIPv6; ++ } ++ else ++ { ++ return EthernetInterface::IPAllowed::IPv4AndIPv6; ++ } ++} ++EthernetInterface::IPAllowed ++ EthernetInterface::iPAddressEnables(EthernetInterface::IPAllowed iPAllowed) ++{ ++ if (iPAllowed == EthernetInterfaceIntf::iPAddressEnables()) ++ { ++ return iPAllowed; ++ } ++ ++ EthernetInterfaceIntf::iPAddressEnables(iPAllowed); ++ writeConfigurationFile(); + ++ return iPAllowed; ++} + ServerList EthernetInterface::getNTPServersFromConf() + { + fs::path confPath = manager.getConfDir(); +@@ -515,7 +599,8 @@ void EthernetInterface::writeConfigurationFile() + #else + stream << "LinkLocalAddressing=no\n"; + #endif +- stream << "IPv6AcceptRA=false\n"; ++ stream << std::boolalpha ++ << "IPv6AcceptRA=" << EthernetInterfaceIntf::iPv6AcceptRA() << "\n"; + + // Add the VLAN entry + for (const auto& intf : vlanInterfaces) +@@ -524,8 +609,24 @@ void EthernetInterface::writeConfigurationFile() + << "\n"; + } + // Add the DHCP entry +- auto value = dHCPEnabled() ? "true"s : "false"s; +- stream << "DHCP="s + value + "\n"; ++ std::string dhcpValue = "false"; ++ if (dHCPEnabled()) ++ { ++ IPAllowed ipAllowed = EthernetInterfaceIntf::iPAddressEnables(); ++ if (ipAllowed == IPAllowed::IPv4AndIPv6) ++ { ++ dhcpValue = "true"; ++ } ++ else if (ipAllowed == IPAllowed::IPv4Only) ++ { ++ dhcpValue = "ipv4"; ++ } ++ else if (ipAllowed == IPAllowed::IPv6Only) ++ { ++ dhcpValue = "ipv6"; ++ } ++ } ++ stream << "DHCP=" << dhcpValue << "\n"; + + // When the interface configured as dhcp, we don't need below given entries + // in config file. +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index d62ca34..7116b47 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -205,7 +205,24 @@ class EthernetInterface : public Ifaces + const std::string& gateway); + + /** @brief write the dhcp section **/ +- void writeDHCPSection(std::fstream& stream);; ++ void writeDHCPSection(std::fstream& stream); ++ ++ /** @brief get the IPv6AcceptRA flag from the network configuration file ++ * ++ */ ++ bool getIPv6AcceptRAFromConf(); ++ ++ /** @brief check conf file for Router Advertisements ++ * ++ */ ++ bool iPv6AcceptRA(bool value) override; ++ ++ /** @brief get the allowed network modes. Similar to DHCP enabled, but ++ * more specific ++ */ ++ IPAllowed getIPAddressEnablesFromConf(); ++ ++ IPAllowed iPAddressEnables(IPAllowed) override; + + /** @brief get the NTP server list from the network conf + * +diff --git a/util.cpp b/util.cpp +index b66f908..9f06e2e 100644 +--- a/util.cpp ++++ b/util.cpp +@@ -405,8 +405,7 @@ bool getDHCPValue(const std::string& confDir, const std::string& intf) + entry("RC=%d", rc)); + return dhcp; + } +- // There will be only single value for DHCP key. +- if (values[0] == "true") ++ if (values[0] != "false") + { + dhcp = true; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network/0003-Adding-channel-specific-privilege-to-network.patch b/meta-openbmc-mods/meta-common/recipes-network/network/network/0003-Adding-channel-specific-privilege-to-network.patch new file mode 100755 index 000000000..4610b8b32 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network/0003-Adding-channel-specific-privilege-to-network.patch @@ -0,0 +1,800 @@ +From 64fff77b31de705a42c5061e9d14946255c6aca1 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Wed, 5 Sep 2018 14:16:54 +0530 +Subject: [PATCH] Adding channel specific privilege to network + + - Adding the channel access information to the network + interface object. This privilege will be used in + channel specific authorization. + - Get supported priv from user manager service dynamically. + - Signal handling for capturing the supported priv list + changes from user managerment. + +Tested-by: +Verified channel access through ipmitool get/set channel +access command + +Change-Id: I3b592a19363eef684e31d5f7c34dad8f2f9211df +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + ethernet_interface.cpp | 119 +++++++++++++- + ethernet_interface.hpp | 433 ++++++++++++++++++++++++++----------------------- + network_manager.cpp | 104 ++++++++++++ + network_manager.hpp | 9 + + 4 files changed, 464 insertions(+), 201 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index 9437b4c..6d23b3d 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -35,6 +35,9 @@ using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + using Argument = xyz::openbmc_project::Common::InvalidArgument; + ++static constexpr const char* networkChannelCfgFile = ++ "/var/channel_intf_data.json"; ++static constexpr const char* defaultChannelPriv = "priv-admin"; + EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + const std::string& objPath, + bool dhcpEnabled, Manager& parent, +@@ -51,6 +54,7 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + MacAddressIntf::mACAddress(getMACAddress(intfName)); + EthernetInterfaceIntf::nTPServers(getNTPServersFromConf()); + EthernetInterfaceIntf::nameservers(getNameServerFromConf()); ++ getChannelPrivilege(intfName); + + // Emit deferred signal. + if (emitSignal) +@@ -208,8 +212,7 @@ InterfaceInfo EthernetInterface::getInterfaceInfo() const + * @return macaddress on success + */ + +-std::string EthernetInterface::getMACAddress( +- const std::string& interfaceName) ++std::string EthernetInterface::getMACAddress(const std::string& interfaceName) + { + ifreq ifr{}; + char macAddress[mac_address::size]{}; +@@ -829,5 +832,117 @@ void EthernetInterface::deleteAll() + manager.writeToConfigurationFile(); + } + ++nlohmann::json EthernetInterface::readJsonFile(const std::string& configFile) ++{ ++ std::ifstream jsonFile(configFile); ++ if (!jsonFile.good()) ++ { ++ log<level::ERR>("JSON file not found"); ++ return nullptr; ++ } ++ ++ nlohmann::json data = nullptr; ++ try ++ { ++ data = nlohmann::json::parse(jsonFile, nullptr, false); ++ } ++ catch (nlohmann::json::parse_error& e) ++ { ++ log<level::DEBUG>("Corrupted channel config.", ++ entry("MSG: %s", e.what())); ++ throw std::runtime_error("Corrupted channel config file"); ++ } ++ ++ return data; ++} ++ ++int EthernetInterface::writeJsonFile(const std::string& configFile, ++ const nlohmann::json& jsonData) ++{ ++ std::ofstream jsonFile(configFile); ++ if (!jsonFile.good()) ++ { ++ log<level::ERR>("JSON file open failed", ++ entry("FILE=%s", networkChannelCfgFile)); ++ return -1; ++ } ++ ++ // Write JSON to file ++ jsonFile << jsonData; ++ ++ jsonFile.flush(); ++ return 0; ++} ++ ++std::string ++ EthernetInterface::getChannelPrivilege(const std::string& interfaceName) ++{ ++ std::string priv(defaultChannelPriv); ++ std::string retPriv; ++ ++ nlohmann::json jsonData = readJsonFile(networkChannelCfgFile); ++ if (jsonData != nullptr) ++ { ++ try ++ { ++ priv = jsonData[interfaceName].get<std::string>(); ++ retPriv = ChannelAccessIntf::maxPrivilege(std::move(priv)); ++ return retPriv; ++ } ++ catch (const nlohmann::json::exception& e) ++ { ++ jsonData[interfaceName] = priv; ++ } ++ } ++ else ++ { ++ jsonData[interfaceName] = priv; ++ } ++ ++ if (writeJsonFile(networkChannelCfgFile, jsonData) != 0) ++ { ++ log<level::DEBUG>("Error in write JSON data to file", ++ entry("FILE=%s", networkChannelCfgFile)); ++ elog<InternalFailure>(); ++ } ++ ++ retPriv = ChannelAccessIntf::maxPrivilege(std::move(priv)); ++ ++ return retPriv; ++} ++ ++std::string EthernetInterface::maxPrivilege(std::string priv) ++{ ++ std::string intfName = interfaceName(); ++ ++ if (!priv.empty() && (std::find(manager.supportedPrivList.begin(), ++ manager.supportedPrivList.end(), ++ priv) == manager.supportedPrivList.end())) ++ { ++ log<level::ERR>("Invalid privilege"); ++ elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"), ++ Argument::ARGUMENT_VALUE(priv.c_str())); ++ } ++ ++ if (ChannelAccessIntf::maxPrivilege() == priv) ++ { ++ // No change in privilege so just return. ++ return priv; ++ } ++ ++ nlohmann::json jsonData = readJsonFile(networkChannelCfgFile); ++ jsonData[intfName] = priv; ++ ++ if (writeJsonFile(networkChannelCfgFile, jsonData) != 0) ++ { ++ log<level::DEBUG>("Error in write JSON data to file", ++ entry("FILE=%s", networkChannelCfgFile)); ++ elog<InternalFailure>(); ++ } ++ ++ // Property change signal will be sent ++ return ChannelAccessIntf::maxPrivilege(std::move(priv)); ++} ++ + } // namespace network + } // namespace phosphor +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index 7116b47..7b1da9a 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -2,10 +2,13 @@ + + #include "types.hpp" + #include "util.hpp" ++#include "xyz/openbmc_project/Channel/ChannelAccess/server.hpp" + #include "xyz/openbmc_project/Network/IP/Create/server.hpp" + + #include <experimental/filesystem> ++#include <nlohmann/json.hpp> + #include <sdbusplus/bus.hpp> ++#include <sdbusplus/bus/match.hpp> + #include <sdbusplus/server/object.hpp> + #include <string> + #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp> +@@ -21,7 +24,8 @@ using Ifaces = sdbusplus::server::object::object< + sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface, + sdbusplus::xyz::openbmc_project::Network::server::MACAddress, + sdbusplus::xyz::openbmc_project::Network::IP::server::Create, +- sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>; ++ sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll, ++ sdbusplus::xyz::openbmc_project::Channel::server::ChannelAccess>; + + using IP = sdbusplus::xyz::openbmc_project::Network::server::IP; + +@@ -29,9 +33,14 @@ using EthernetInterfaceIntf = + sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface; + using MacAddressIntf = + sdbusplus::xyz::openbmc_project::Network::server::MACAddress; ++using ChannelAccessIntf = ++ sdbusplus::xyz::openbmc_project::Channel::server::ChannelAccess; + + using ServerList = std::vector<std::string>; + ++using DbusVariant = ++ sdbusplus::message::variant<std::string, std::vector<std::string>>; ++ + namespace fs = std::experimental::filesystem; + + class Manager; // forward declaration of network manager. +@@ -59,204 +68,230 @@ using VlanInterfaceMap = + */ + class EthernetInterface : public Ifaces + { +- public: +- EthernetInterface() = delete; +- EthernetInterface(const EthernetInterface&) = delete; +- EthernetInterface& operator=(const EthernetInterface&) = delete; +- EthernetInterface(EthernetInterface&&) = delete; +- EthernetInterface& operator=(EthernetInterface&&) = delete; +- virtual ~EthernetInterface() = default; +- +- /** @brief Constructor to put object onto bus at a dbus path. +- * @param[in] bus - Bus to attach to. +- * @param[in] objPath - Path to attach at. +- * @param[in] dhcpEnabled - is dhcp enabled(true/false). +- * @param[in] parent - parent object. +- * @param[in] emitSignal - true if the object added signal needs to be +- * send. +- */ +- EthernetInterface(sdbusplus::bus::bus& bus, +- const std::string& objPath, +- bool dhcpEnabled, +- Manager& parent, +- bool emitSignal = true); +- +- /** @brief Function to create ipaddress dbus object. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress- IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway ip address. +- */ +- +- void iP(IP::Protocol addressType, +- std::string ipaddress, +- uint8_t prefixLength, +- std::string gateway) override; +- +- /* @brief delete the dbus object of the given ipaddress. +- * @param[in] ipaddress - IP address. +- */ +- void deleteObject(const std::string& ipaddress); +- +- /* @brief delete the vlan dbus object of the given interface. +- * Also deletes the device file and the network file. +- * @param[in] interface - VLAN Interface. +- */ +- void deleteVLANObject(const std::string& interface); +- +- /* @brief creates the dbus object(IPaddres) given in the address list. +- * @param[in] addrs - address list for which dbus objects needs +- * to create. +- */ +- void createIPAddressObjects(); +- +- /* @brief Gets all the ip addresses. +- * @returns the list of ipaddress. +- */ +- const AddressMap& getAddresses() const { return addrs; } +- +- /** Set value of DHCPEnabled */ +- bool dHCPEnabled(bool value) override; +- +- /** @brief sets the MAC address. +- * @param[in] value - MAC address which needs to be set on the system. +- * @returns macAddress of the interface or throws an error. +- */ +- std::string mACAddress(std::string value) override; +- +- /** @brief sets the NTP servers. +- * @param[in] value - vector of NTP servers. +- */ +- ServerList nTPServers(ServerList value) override; +- +- /** @brief sets the DNS/nameservers. +- * @param[in] value - vector of DNS servers. +- */ +- ServerList nameservers(ServerList value) override; +- +- /** @brief create Vlan interface. +- * @param[in] id- VLAN identifier. +- */ +- void createVLAN(VlanId id); +- +- /** @brief load the vlan info from the system +- * and creates the ip address dbus objects. +- * @param[in] vlanID- VLAN identifier. +- */ +- void loadVLAN(VlanId vlanID); +- +- /** @brief write the network conf file with the in-memory objects. +- */ +- void writeConfigurationFile(); +- +- /** @brief delete all dbus objects. +- */ +- void deleteAll(); +- +- /** @brief get the mac address of the interface. +- * @param[in] interfaceName - Network interface name. +- * @return macaddress on success +- */ +- +- static std::string getMACAddress(const std::string& interfaceName); +- +- using EthernetInterfaceIntf::dHCPEnabled; +- using EthernetInterfaceIntf::interfaceName; +- using MacAddressIntf::mACAddress; +- +- /** @brief Absolute path of the resolv conf file */ +- static constexpr auto resolvConfFile = "/etc/resolv.conf"; +- +- protected: +- /** @brief get the info of the ethernet interface. +- * @return tuple having the link speed,autonegotiation,duplexmode . +- */ +- InterfaceInfo getInterfaceInfo() const; +- +- /* @brief delete the vlan interface from system. +- * @param[in] interface - vlan Interface. +- */ +- void deleteVLANFromSystem(const std::string& interface); +- +- /** @brief construct the ip address dbus object path. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- +- * @return path of the address object. +- */ +- +- std::string generateObjectPath(IP::Protocol addressType, +- const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway) const; +- +- /** @brief generates the id by doing hash of ipaddress, +- * prefixlength and the gateway. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- * @return hash string. +- */ +- +- static std::string generateId(const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway); +- +- /** @brief write the dhcp section **/ +- void writeDHCPSection(std::fstream& stream); +- +- /** @brief get the IPv6AcceptRA flag from the network configuration file +- * +- */ +- bool getIPv6AcceptRAFromConf(); +- +- /** @brief check conf file for Router Advertisements +- * +- */ +- bool iPv6AcceptRA(bool value) override; +- +- /** @brief get the allowed network modes. Similar to DHCP enabled, but +- * more specific +- */ +- IPAllowed getIPAddressEnablesFromConf(); +- +- IPAllowed iPAddressEnables(IPAllowed) override; +- +- /** @brief get the NTP server list from the network conf +- * +- */ +- ServerList getNTPServersFromConf(); +- +- /** @brief write the DNS entries to resolver file. +- * @param[in] dnsList - DNS server list which needs to be written. +- * @param[in] file - File to write the name server entries to. +- */ +- void writeDNSEntries(const ServerList& dnsList, +- const std::string& file); +- +- /** @brief get the name server details from the network conf +- * +- */ +- ServerList getNameServerFromConf(); +- +- /** @brief Persistent sdbusplus DBus bus connection. */ +- sdbusplus::bus::bus& bus; +- +- /** @brief Network Manager object. */ +- Manager& manager; +- +- /** @brief Persistent map of IPAddress dbus objects and their names */ +- AddressMap addrs; +- +- /** @brief Persistent map of VLAN interface dbus objects and their names */ +- VlanInterfaceMap vlanInterfaces; +- +- /** @brief Dbus object path */ +- std::string objPath; +- +- friend class TestEthernetInterface; ++ public: ++ EthernetInterface() = delete; ++ EthernetInterface(const EthernetInterface&) = delete; ++ EthernetInterface& operator=(const EthernetInterface&) = delete; ++ EthernetInterface(EthernetInterface&&) = delete; ++ EthernetInterface& operator=(EthernetInterface&&) = delete; ++ virtual ~EthernetInterface() = default; ++ ++ /** @brief Constructor to put object onto bus at a dbus path. ++ * @param[in] bus - Bus to attach to. ++ * @param[in] objPath - Path to attach at. ++ * @param[in] dhcpEnabled - is dhcp enabled(true/false). ++ * @param[in] parent - parent object. ++ * @param[in] emitSignal - true if the object added signal needs to be ++ * send. ++ */ ++ EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath, ++ bool dhcpEnabled, Manager& parent, ++ bool emitSignal = true); ++ ++ /** @brief Function to create ipaddress dbus object. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress- IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway ip address. ++ */ ++ ++ void iP(IP::Protocol addressType, std::string ipaddress, ++ uint8_t prefixLength, std::string gateway) override; ++ ++ /* @brief delete the dbus object of the given ipaddress. ++ * @param[in] ipaddress - IP address. ++ */ ++ void deleteObject(const std::string& ipaddress); ++ ++ /* @brief delete the vlan dbus object of the given interface. ++ * Also deletes the device file and the network file. ++ * @param[in] interface - VLAN Interface. ++ */ ++ void deleteVLANObject(const std::string& interface); ++ ++ /* @brief creates the dbus object(IPaddres) given in the address list. ++ * @param[in] addrs - address list for which dbus objects needs ++ * to create. ++ */ ++ void createIPAddressObjects(); ++ ++ /* @brief Gets all the ip addresses. ++ * @returns the list of ipaddress. ++ */ ++ const AddressMap& getAddresses() const ++ { ++ return addrs; ++ } ++ ++ /** Set value of DHCPEnabled */ ++ bool dHCPEnabled(bool value) override; ++ ++ /** @brief sets the MAC address. ++ * @param[in] value - MAC address which needs to be set on the system. ++ * @returns macAddress of the interface or throws an error. ++ */ ++ std::string mACAddress(std::string value) override; ++ ++ /** @brief sets the NTP servers. ++ * @param[in] value - vector of NTP servers. ++ */ ++ ServerList nTPServers(ServerList value) override; ++ ++ /** @brief sets the DNS/nameservers. ++ * @param[in] value - vector of DNS servers. ++ */ ++ ServerList nameservers(ServerList value) override; ++ ++ /** @brief create Vlan interface. ++ * @param[in] id- VLAN identifier. ++ */ ++ void createVLAN(VlanId id); ++ ++ /** @brief load the vlan info from the system ++ * and creates the ip address dbus objects. ++ * @param[in] vlanID- VLAN identifier. ++ */ ++ void loadVLAN(VlanId vlanID); ++ ++ /** @brief write the network conf file with the in-memory objects. ++ */ ++ void writeConfigurationFile(); ++ ++ /** @brief delete all dbus objects. ++ */ ++ void deleteAll(); ++ ++ /** @brief get the mac address of the interface. ++ * @param[in] interfaceName - Network interface name. ++ * @return macaddress on success ++ */ ++ ++ static std::string getMACAddress(const std::string& interfaceName); ++ ++ /** @brief sets the channel maxium privilege. ++ * @param[in] value - Channel privilege which needs to be set on the ++ * system. ++ * @returns privilege of the interface or throws an error. ++ */ ++ std::string maxPrivilege(std::string value) override; ++ ++ using ChannelAccessIntf::maxPrivilege; ++ using EthernetInterfaceIntf::dHCPEnabled; ++ using EthernetInterfaceIntf::interfaceName; ++ using MacAddressIntf::mACAddress; ++ ++ /** @brief Absolute path of the resolv conf file */ ++ static constexpr auto resolvConfFile = "/etc/resolv.conf"; ++ ++ protected: ++ /** @brief get the info of the ethernet interface. ++ * @return tuple having the link speed,autonegotiation,duplexmode . ++ */ ++ InterfaceInfo getInterfaceInfo() const; ++ ++ /* @brief delete the vlan interface from system. ++ * @param[in] interface - vlan Interface. ++ */ ++ void deleteVLANFromSystem(const std::string& interface); ++ ++ /** @brief construct the ip address dbus object path. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ ++ * @return path of the address object. ++ */ ++ ++ std::string generateObjectPath(IP::Protocol addressType, ++ const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway) const; ++ ++ /** @brief generates the id by doing hash of ipaddress, ++ * prefixlength and the gateway. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ * @return hash string. ++ */ ++ ++ static std::string generateId(const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway); ++ ++ /** @brief write the dhcp section **/ ++ void writeDHCPSection(std::fstream& stream); ++ ++ /** @brief get the IPv6AcceptRA flag from the network configuration file ++ * ++ */ ++ bool getIPv6AcceptRAFromConf(); ++ ++ /** @brief check conf file for Router Advertisements ++ * ++ */ ++ bool iPv6AcceptRA(bool value) override; ++ ++ /** @brief get the allowed network modes. Similar to DHCP enabled, but ++ * more specific ++ */ ++ IPAllowed getIPAddressEnablesFromConf(); ++ ++ IPAllowed iPAddressEnables(IPAllowed) override; ++ ++ /** @brief get the NTP server list from the network conf ++ * ++ */ ++ ServerList getNTPServersFromConf(); ++ ++ /** @brief write the DNS entries to resolver file. ++ * @param[in] dnsList - DNS server list which needs to be written. ++ * @param[in] file - File to write the name server entries to. ++ */ ++ void writeDNSEntries(const ServerList& dnsList, const std::string& file); ++ ++ /** @brief get the name server details from the network conf ++ * ++ */ ++ ServerList getNameServerFromConf(); ++ ++ /** @brief Persistent sdbusplus DBus bus connection. */ ++ sdbusplus::bus::bus& bus; ++ ++ /** @brief Network Manager object. */ ++ Manager& manager; ++ ++ /** @brief Persistent map of IPAddress dbus objects and their names */ ++ AddressMap addrs; ++ ++ /** @brief Persistent map of VLAN interface dbus objects and their names */ ++ VlanInterfaceMap vlanInterfaces; ++ ++ /** @brief Dbus object path */ ++ std::string objPath; ++ ++ friend class TestEthernetInterface; ++ ++ /** @brief gets the channel privilege. ++ * @param[in] interfaceName - Network interface name. ++ * @returns privilege of the interface ++ */ ++ std::string getChannelPrivilege(const std::string& interfaceName); ++ ++ /** @brief reads the channel access info from file. ++ * @param[in] configFile - channel access filename ++ * @returns json file data ++ */ ++ nlohmann::json readJsonFile(const std::string& configFile); ++ ++ /** @brief writes the channel access info to file. ++ * @param[in] configFile - channel access filename ++ * @param[in] jsonData - json data to write ++ * @returns success or failure ++ */ ++ int writeJsonFile(const std::string& configFile, ++ const nlohmann::json& jsonData); + }; + + } // namespace network +diff --git a/network_manager.cpp b/network_manager.cpp +index c4ab0da..c573d01 100644 +--- a/network_manager.cpp ++++ b/network_manager.cpp +@@ -30,6 +30,13 @@ extern std::unique_ptr<Timer> restartTimer; + using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + ++static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user"; ++static constexpr const char* userMgrInterface = ++ "xyz.openbmc_project.User.Manager"; ++static constexpr const char* propNameAllPrivileges = "AllPrivileges"; ++ ++std::unique_ptr<sdbusplus::bus::match_t> usrMgmtSignal(nullptr); ++ + Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath, + const std::string& path) : + details::VLANCreateIface(bus, objPath, true), +@@ -37,6 +44,103 @@ Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath, + { + fs::path confDir(path); + setConfDir(confDir); ++ initSupportedPrivilges(); ++} ++ ++std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf, ++ const std::string& path) ++{ ++ auto mapperCall = ++ bus.new_method_call("xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject"); ++ ++ mapperCall.append(path); ++ mapperCall.append(std::vector<std::string>({intf})); ++ ++ auto mapperResponseMsg = bus.call(mapperCall); ++ ++ std::map<std::string, std::vector<std::string>> mapperResponse; ++ mapperResponseMsg.read(mapperResponse); ++ ++ if (mapperResponse.begin() == mapperResponse.end()) ++ { ++ throw std::runtime_error("ERROR in reading the mapper response"); ++ } ++ ++ return mapperResponse.begin()->first; ++} ++ ++std::string Manager::getUserServiceName() ++{ ++ static std::string userMgmtService; ++ if (userMgmtService.empty()) ++ { ++ try ++ { ++ userMgmtService = ++ getUserService(bus, userMgrInterface, userMgrObjBasePath); ++ } ++ catch (const std::exception& e) ++ { ++ log<level::ERR>("Exception caught in getUserServiceName."); ++ userMgmtService.clear(); ++ } ++ } ++ return userMgmtService; ++} ++ ++void Manager::initSupportedPrivilges() ++{ ++ std::string userServiceName = getUserServiceName(); ++ if (!userServiceName.empty()) ++ { ++ auto method = bus.new_method_call( ++ getUserServiceName().c_str(), userMgrObjBasePath, ++ "org.freedesktop.DBus.Properties", "Get"); ++ method.append(userMgrInterface, propNameAllPrivileges); ++ ++ auto reply = bus.call(method); ++ if (reply.is_method_error()) ++ { ++ log<level::DEBUG>("get-property AllPrivileges failed", ++ entry("OBJPATH:%s", userMgrObjBasePath), ++ entry("INTERFACE:%s", userMgrInterface)); ++ return; ++ } ++ ++ sdbusplus::message::variant<std::vector<std::string>> result; ++ reply.read(result); ++ ++ supportedPrivList = ++ sdbusplus::message::variant_ns::get<std::vector<std::string>>( ++ result); ++ } ++ ++ // Resgister the signal ++ if (usrMgmtSignal == nullptr) ++ { ++ log<level::DEBUG>("Registering User.Manager propertychange signal."); ++ usrMgmtSignal = std::make_unique<sdbusplus::bus::match_t>( ++ bus, ++ sdbusplus::bus::match::rules::propertiesChanged(userMgrObjBasePath, ++ userMgrInterface), ++ [&](sdbusplus::message::message& msg) { ++ log<level::DEBUG>("UserMgr properties changed signal"); ++ std::map<std::string, DbusVariant> props; ++ std::string iface; ++ msg.read(iface, props); ++ for (const auto& t : props) ++ { ++ if (t.first == propNameAllPrivileges) ++ { ++ supportedPrivList = sdbusplus::message::variant_ns::get< ++ std::vector<std::string>>(t.second); ++ } ++ } ++ }); ++ } ++ return; + } + + bool Manager::createDefaultNetworkFiles(bool force) +diff --git a/network_manager.hpp b/network_manager.hpp +index e2dfea9..22eef04 100644 +--- a/network_manager.hpp ++++ b/network_manager.hpp +@@ -137,6 +137,9 @@ class Manager : public details::VLANCreateIface + return (interfaces.find(intf) != interfaces.end()); + } + ++ /** supported privilege list **/ ++ std::vector<std::string> supportedPrivList; ++ + protected: + /** @brief Persistent sdbusplus DBus bus connection. */ + sdbusplus::bus::bus& bus; +@@ -159,6 +162,12 @@ class Manager : public details::VLANCreateIface + + /** @brief Network Configuration directory. */ + fs::path confDir; ++ ++ /** Get the user management service name dynamically **/ ++ std::string getUserServiceName(); ++ ++ /** @brief initializes the supportedPrivilege List */ ++ void initSupportedPrivilges(); + }; + + } // namespace network +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network_%.bbappend b/meta-openbmc-mods/meta-common/recipes-network/network/network_%.bbappend new file mode 100644 index 000000000..275051e19 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +DEPENDS += "nlohmann-json" + +SRC_URI += "file://0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch \ + file://0002-IPv6-Network-changes-to-configuration-file.patch \ + file://0003-Adding-channel-specific-privilege-to-network.patch \ + " + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/xyz.openbmc_project.CloseMuxes.service b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/xyz.openbmc_project.CloseMuxes.service new file mode 100644 index 000000000..bc05fa1a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/xyz.openbmc_project.CloseMuxes.service @@ -0,0 +1,10 @@ +[Unit] +Description=Close Muxes +ConditionFileNotEmpty=/usr/bin/CloseMuxes.py + +[Service] +ExecStart=/usr/bin/env CloseMuxes.py +Type=oneshot + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend new file mode 100644 index 000000000..b059a35d7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend @@ -0,0 +1,18 @@ +# this is here just to bump faster than upstream +SRC_URI = "git://github.com/openbmc/entity-manager.git" +SRCREV = "4133426a04bf46e275416362205bc29c1b3c0935" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +RDEPENDS_${PN} += "python" +SRC_URI += " file://xyz.openbmc_project.CloseMuxes.service" +SYSTEMD_SERVICE_${PN} += " xyz.openbmc_project.CloseMuxes.service" + +EXTRA_OECMAKE = "-DYOCTO=1 -DUSE_OVERLAYS=0" + +do_install_prepend() { + install -d ${D}${bindir} + install -m 0755 ${S}/scripts/CloseMuxes.py ${D}${bindir} + install -d ${D}${base_libdir}/systemd/system + install -m 0644 ${WORKDIR}/xyz.openbmc_project.CloseMuxes.service ${D}${base_libdir}/systemd/system +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0002-Modify-Dbus-for-IPv6.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0002-Modify-Dbus-for-IPv6.patch new file mode 100644 index 000000000..5b86d3154 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0002-Modify-Dbus-for-IPv6.patch @@ -0,0 +1,55 @@ +From 066ecddebc29a87b05f8c66491eec19bb27d1d33 Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Wed, 6 Jun 2018 10:11:58 -0700 +Subject: [PATCH 3/3] Modify Dbus for IPv6. + +Add additional interfaces for IPv6 use. +--- + .../Network/EthernetInterface.interface.yaml | 18 ++++++++++++++++++ + xyz/openbmc_project/Network/IP.interface.yaml | 4 ++++ + 2 files changed, 22 insertions(+) + +diff --git a/xyz/openbmc_project/Network/EthernetInterface.interface.yaml b/xyz/openbmc_project/Network/EthernetInterface.interface.yaml +index fc744fc..fd19e27 100644 +--- a/xyz/openbmc_project/Network/EthernetInterface.interface.yaml ++++ b/xyz/openbmc_project/Network/EthernetInterface.interface.yaml +@@ -37,3 +37,21 @@ properties: + Implementation of this Dbus-interface is required to implement this property. + This property supports read/write operation. + Configure the NTP servers on the system during write operation. ++ - name: IPv6AcceptRA ++ type: boolean ++ description: > ++ Boolean for accepting router advertisements in IPv6 ++ - name: IPAddressEnables ++ type: enum[self.IPAllowed] ++ description: > ++ The type of IP connection is allowed on this channel ++ ++enumerations: ++ - name: IPAllowed ++ description: > ++ Determines whether the system allows both IPv6 & IPv4, or disables on ++ or the other ++ values: ++ - name: IPv4AndIPv6 ++ - name: IPv4Only ++ - name: IPv6Only +diff --git a/xyz/openbmc_project/Network/IP.interface.yaml b/xyz/openbmc_project/Network/IP.interface.yaml +index 565dcd6..2ffb016 100644 +--- a/xyz/openbmc_project/Network/IP.interface.yaml ++++ b/xyz/openbmc_project/Network/IP.interface.yaml +@@ -22,6 +22,10 @@ properties: + type: string + description: > + This is the IP gateway for this address. ++ - name: BackupGateway ++ type: string ++ description: > ++ This is the IP address of the backup gateway. + - name: Type + type: enum[self.Protocol] + description: > +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0003-Chassis-Power-Control-are-implemented.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0003-Chassis-Power-Control-are-implemented.patch new file mode 100644 index 000000000..79d02ca9b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0003-Chassis-Power-Control-are-implemented.patch @@ -0,0 +1,257 @@ +From 35271230690c5d85dc7a6502031b38d93ddd683f Mon Sep 17 00:00:00 2001 +From: Ed Tanous <ed.tanous@intel.com> +Date: Thu, 24 Jan 2019 09:29:01 -0800 +Subject: [PATCH] Chassis Power Control are implemented. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Feature level: +Power on server is ready +Power off server is ready +Power cycle server is ready +Power reset server is ready + +Framework level: +WebUI is enabled. +IPMI Commands is enabled. +Restful API is enabled. +Physical buttons (Power/Reset/ID) are enabled. +Chassis state manager is enabled. +Host state manager is enabled. + +Enabled IPMI commands: +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis status +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power status +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power on +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power off +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power cycle +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power reset + +Enabled Restful APIs: +Login: curl --noproxy <ip_addr> -c cjar -b cjar -k -H "Content-Type: application/json" -X POST https://<ip_addr>/login -d "{\"data\": [ \"root\", \"0penBmc\" ] }“ +Host State: curl --noproxy <ip_addr> -b cjar -k https://<ip_addr>/xyz/openbmc_project/state/host0 +Chassis State: curl --noproxy <ip_addr> -b cjar -k https://<ip_addr>/xyz/openbmc_project/state/chassis0 +Power state: curl --noproxy <ip_addr> -b cjar -k https://<ip_addr>/xyz/openbmc_project/Chassis/Control/Power0 +Power on/off: curl --noproxy <ip_addr> -q1c cjar -b cjar -k -H "Content-Type: application/json" -d '{"data": "xyz.openbmc_project.State.Host.Transition.Off"}' -X PUT https://<ip_addr>/xyz/openbmc_project/state/host0/attr/RequestedHostTransition + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + .../Chassis/Control/Chassis.interface.yaml | 94 +++++++++++++++++++ + .../Chassis/Control/Power.interface.yaml | 85 ++++++++++++++--- + 2 files changed, 166 insertions(+), 13 deletions(-) + create mode 100644 xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml + +diff --git a/xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml b/xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml +new file mode 100644 +index 0000000..c28492a +--- /dev/null ++++ b/xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml +@@ -0,0 +1,94 @@ ++description: > ++ chassis control service ++methods: ++ - name: powerOn ++ description: > ++ Power on system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: powerOff ++ description: > ++ Power Off system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: softPowerOff ++ description: > ++ Soft Power off system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: reboot ++ description: > ++ reboot system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: softReboot ++ description: > ++ Soft Reboot system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: quiesce ++ description: > ++ Quiesce system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: getPowerState ++ description: > ++ Get system power state. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ +diff --git a/xyz/openbmc_project/Chassis/Control/Power.interface.yaml b/xyz/openbmc_project/Chassis/Control/Power.interface.yaml +index 082586f..e77598b 100644 +--- a/xyz/openbmc_project/Chassis/Control/Power.interface.yaml ++++ b/xyz/openbmc_project/Chassis/Control/Power.interface.yaml +@@ -1,31 +1,90 @@ + description: > +- Power control service ++ Chassis control service + methods: ++ - name: setPowerState ++ description: > ++ set host power state. ++ parameters: ++ - name: state ++ type: int32 ++ description: > ++ 0 for force power off host ++ 1 for power on host ++ returns: ++ - name: status ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: getPowerState ++ description: > ++ Get current host power status. ++ returns: ++ - name: status ++ type: int32 ++ description: > ++ Current host status, ++ 0 for host power off ++ 1 for host power on ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ + - name: forcePowerOff + description: > + Force power off the host. + returns: + - name: status +- type: boolean ++ type: int32 + description: > + The result of power off command. + errors: + - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ + - xyz.openbmc_project.Chassis.Common.Error.IOError + + properties: +- - name: PGood +- type: boolean +- default: false ++ - name: vrd_good ++ type: int32 ++ default: 0 + description: > +- PSU Power good property +- It is a read-only property. +- - name: State ++ ACPI status ++ - name: s4s5_state + type: int32 + default: 0 + description: > +- System power status +- 0: power is off +- 1: power is on +- Setting its value to change the system state +- Read its value to get the system state. +\ No newline at end of file ++ ACPI status ++ - name: pgood ++ type: int32 ++ default: 0 ++ description: > ++ pgood property ++ - name: state ++ type: int32 ++ default: 0 ++ description: > ++ state property ++ - name: pgood_timeout ++ type: int32 ++ default: 0 ++ description: > ++ pgoodtimeout property ++ - name: post_complete ++ type: boolean ++ default: false ++ description: > ++ The current BIOS POST state, ++ false means not completed or system reset, ++ true means BIOS POST completed. ++signals: ++ - name: PowerGood ++ description: > ++ Signal for powergood ++ - name: PowerLost ++ description: > ++ Signal for powerlost +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch new file mode 100644 index 000000000..7568f8ce9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch @@ -0,0 +1,120 @@ +From a30a09f58b9ebfb267c0b9cce9ae25994ea025ca Mon Sep 17 00:00:00 2001 +From: cyang29 <cheng.c.yang@intel.com> +Date: Tue, 17 Jul 2018 16:04:58 +0800 +Subject: [PATCH] Add DBUS interface of CPU and Memory's properties Feature + Support: SMBIOS service interface. CPU DIMM information redfish + interface. Base on smbios spec DSP0134_3.0.0 + +Signed-off-by: cyang29 <cheng.c.yang@intel.com> +--- + .../Inventory/Item/Cpu.interface.yaml | 41 +++++++++++++++++++ + .../Inventory/Item/Dimm.interface.yaml | 46 +++++++++++++++++++++- + 2 files changed, 86 insertions(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml b/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml +index ab29cf3..313eada 100644 +--- a/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml ++++ b/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml +@@ -1,4 +1,45 @@ + description: > + Implement to provide CPU attributes. ++properties: ++ - name: ProcessorSocket ++ type: string ++ description: > ++ Processor Socket on MotherBoard ++ - name: ProcessorType ++ type: string ++ description: > ++ Processor Type of CPU ++ - name: ProcessorFamily ++ type: string ++ description: > ++ Processor Family of CPU ++ - name: ProcessorManufacturer ++ type: string ++ description: > ++ Processor Manufacturer of CPU ++ - name: ProcessorId ++ type: uint32 ++ description: > ++ Processor ID of CPU ++ - name: ProcessorVersion ++ type: string ++ description: > ++ Processor Version of CPU ++ - name: ProcessorMaxSpeed ++ type: uint16 ++ description: > ++ Max Speed CPU Can Support ++ - name: ProcessorCharacteristics ++ type: string ++ description: > ++ The Characteristics CPU Has ++ - name: ProcessorCoreCount ++ type: uint16 ++ description: > ++ The Count of Core in CPU ++ - name: ProcessorThreadCount ++ type: uint16 ++ description: > ++ The Count of Thread CPU Can Support + + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +diff --git a/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml b/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml +index d85326d..b750320 100644 +--- a/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml ++++ b/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml +@@ -1,4 +1,48 @@ + description: > + Implement to provide DIMM attributes. +- ++properties: ++ - name: MemoryDataWidth ++ type: uint16 ++ description: > ++ Data width of Memory. ++ - name: MemorySizeInKB ++ type: uint32 ++ description: > ++ Memory size of DIMM in Kilobyte. ++ - name: MemoryDeviceLocator ++ type: string ++ description: > ++ Socket on base board where Memory located. ++ - name: MemoryType ++ type: string ++ description: > ++ Type of memory. ++ - name: MemoryTypeDetail ++ type: string ++ description: > ++ Additional detail on Memory. ++ - name: MemorySpeed ++ type: uint16 ++ description: > ++ The maximun capable speed of Memory. ++ - name: MemoryManufacturer ++ type: string ++ description: > ++ Manufacturer of memory. ++ - name: MemorySerialNum ++ type: string ++ description: > ++ Memory Serial Number. ++ - name: MemoryPartNum ++ type: string ++ description: > ++ Memory Part Number. ++ - name: MemoryAttributes ++ type: byte ++ description: > ++ Rank attributes of Memory. ++ - name: MemoryConfClockSpeed ++ type: uint16 ++ description: > ++ Configured clock speed to Memory. + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0006-dbus-interface-add-boot-option-support-for-floppy-an.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0006-dbus-interface-add-boot-option-support-for-floppy-an.patch new file mode 100644 index 000000000..f0d7b03f8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0006-dbus-interface-add-boot-option-support-for-floppy-an.patch @@ -0,0 +1,77 @@ +From 643772fc7f6021fbfba3b14de0c86501ae3e7f3a Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Fri, 13 Jul 2018 15:22:05 +0800 +Subject: [PATCH] [dbus interface]add boot option support for floppy and USB + +Current implementations use ExternalMedia type to specify both CD/DVD/USB +/Floppy. But in IPMI spec, they are different. CD/DVD type is 0x5 and +USB/Floppy type is 0xF. + +This causes a bug that we can not force BIOS boots into USB/Floppy. + +Test: +$ ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus raw + 0x0 0x8 0x5 0x80 0x14 0x00 0x00 0x00 +$ ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus chassis bootparam get 5 +Boot parameter version: 1 +Boot parameter 5 is valid/unlocked +Boot parameter data: 8014000000 + Boot Flags : + - Boot Flag Valid + - Options apply to only next boot + - BIOS PC Compatible (legacy) boot + - Boot Device Selector : Force Boot from CD/DVD + - Console Redirection control : System Default + - BIOS verbosity : Console redirection occurs per BIOS + configuration setting (default) + - BIOS Mux Control Override : + BIOS uses recommended setting of the mux at the end of POST + +$ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus raw + 0x0 0x8 0x5 0x80 0x3c 0x00 0x00 0x00 +$ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus chassis bootparam get 5 +Boot parameter version: 1 +Boot parameter 5 is valid/unlocked +Boot parameter data: 803c000000 + Boot Flags : + - Boot Flag Valid + - Options apply to only next boot + - BIOS PC Compatible (legacy) boot + - Boot Device Selector : Force Boot from Floppy/primary removable media + - Console Redirection control : System Default + - BIOS verbosity : + Console redirection occurs per BIOS configuration setting (default) + + - BIOS Mux Control Override : + BIOS uses recommended setting of the mux at the end of POST + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + xyz/openbmc_project/Control/Boot/Source.interface.yaml | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/xyz/openbmc_project/Control/Boot/Source.interface.yaml b/xyz/openbmc_project/Control/Boot/Source.interface.yaml +index ea811bd..8e5916f 100644 +--- a/xyz/openbmc_project/Control/Boot/Source.interface.yaml ++++ b/xyz/openbmc_project/Control/Boot/Source.interface.yaml +@@ -15,12 +15,15 @@ enumerations: + - name: Disk + description: > + Boot from the local hard disk. +- - name: ExternalMedia ++ - name: DVD + description: > +- Boot from CD/DVD/USB, etc. ++ Boot from CD/DVD. + - name: Network + description: > + Boot from a remote source over a network. + - name: Default + description: > + Boot from an implementation defined source. ++ - name: Removable ++ description: > ++ Boot from floppy/primary removable media(USB). +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch new file mode 100644 index 000000000..c87b2d89d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch @@ -0,0 +1,32 @@ +From 49debd0955b672d591f35e74119b288bd6df2992 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 24 Jul 2018 11:40:49 +0800 +Subject: [PATCH] [ipmi] set BIOS id + +change#2 +add new dbus interface for BIOS attributes + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + xyz/openbmc_project/Inventory/Item/Bios.interface.yaml | 9 +++++++++ + 1 file changed, 9 insertions(+) + create mode 100644 xyz/openbmc_project/Inventory/Item/Bios.interface.yaml + +diff --git a/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml b/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +new file mode 100644 +index 0000000..d7a6b95 +--- /dev/null ++++ b/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +@@ -0,0 +1,9 @@ ++description: > ++ Implement to provide BIOS attributes. ++properties: ++ - name: BiosId ++ type: string ++ description: > ++ BIOS ID (version) string ++ ++# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0009-Add-host-restart-cause-property.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0009-Add-host-restart-cause-property.patch new file mode 100644 index 000000000..1221a0ab4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0009-Add-host-restart-cause-property.patch @@ -0,0 +1,98 @@ +From 72b7b30a5dda56c170ee2ce82c1082c26f4663e3 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 31 Jul 2018 16:55:21 +0800 +Subject: [PATCH] Add host restart cause property. + +Add host restart cause property to track the information about what +action last caused the system to restart. +According to IPMI Spec, it includes 12 types as following: +1. Unknown 0x0 +2. IpmiCommand 0x1 +3. ResetButton 0x2 +4. PowerButton 0x3 +5. WatchdogTimer 0x4 +6. OEM 0x5 +7. PowerPolicyAlwaysOn 0x6 +8. PowerPolicyPreviousState 0x7 +9. PEF-Reset 0x8 +10. PEF-PowerCycle 0x9 +11. SoftReset 0xA +12. RTC-Wakeup 0xB + +Change-Id: Id2cc6a18b98e485a978940e5ffc085bf5c4fbed8 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + xyz/openbmc_project/State/Host.interface.yaml | 52 +++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/xyz/openbmc_project/State/Host.interface.yaml b/xyz/openbmc_project/State/Host.interface.yaml +index 8f481b8..a4d88d0 100644 +--- a/xyz/openbmc_project/State/Host.interface.yaml ++++ b/xyz/openbmc_project/State/Host.interface.yaml +@@ -17,6 +17,12 @@ properties: + comparing the CurrentHostState and the RequestedHostTransition + properties. + ++ - name: HostRestartCause ++ type: enum[self.RestartCause] ++ default: Unknown ++ description: > ++ The information about what action last caused the system to restart. ++ + enumerations: + - name: Transition + description: > +@@ -45,3 +51,49 @@ enumerations: + - name: 'Quiesced' + description: > + Host firmware is quiesced ++ ++ - name: RestartCause ++ description: > ++ The information about what action last caused the system to restart. ++ values: ++ - name: Unknown ++ description: > ++ System start/restart detected but the reason is unknown. ++ - name: IpmiCommand ++ description: > ++ System start/restart detected and caused by ipmi command. ++ - name: ResetButton ++ description: > ++ System start/restart detected and caused by reset button. ++ - name: PowerButton ++ description: > ++ System start/restart detected and caused by power button. ++ - name: WatchdogTimer ++ description: > ++ System start/restart detected and casued by watchdog expiration. ++ - name: OEM ++ description: > ++ System start/restart detected and caused by OEM command. ++ - name: PowerPolicyAlwaysOn ++ description: > ++ System start/restart detected and caused by power restore policy ++ "chassis always powers up after AC/mains is applied or returns". ++ - name: PowerPolicyPreviousState ++ description: > ++ System start/restart detected and caused by power restore policy ++ "after AC/mains is applied or returns, power is restored to the ++ state was in effect when AC/mains removed or lost". ++ - name: PEFReset ++ description: > ++ System start/restart detected and caused by PEF(reset). ++ - name: PEFPowerCycle ++ description: > ++ System start/restart detected and caused by PEF(power-cycle). ++ - name: SoftReset ++ description: > ++ System start/restart detected and caused by soft reset like ++ "CTRL-ALT-DEL". ++ - name: RTCWakeup ++ description: > ++ System start/restart detected and caused by system real time ++ clock(RTC) wakeup. +\ No newline at end of file +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch new file mode 100644 index 000000000..2c9344306 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch @@ -0,0 +1,34 @@ +From 631deef0ca88a77283741edeae8078d2185f414c Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 10 Aug 2018 16:23:13 +0800 +Subject: [PATCH] Increase the default watchdog timeout value + +The default timeout for poweron is 30 seconds, +but currently the host power on needs 120+ seconds +due to unimplemented ipmi commands for BIOS. + +Increase the value as a workaround, +to avoid the watchdog timeout during power on. +Will adjust this value in the future + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + xyz/openbmc_project/State/Watchdog.interface.yaml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml +index f76dbf2..402e1a8 100644 +--- a/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/xyz/openbmc_project/State/Watchdog.interface.yaml +@@ -37,7 +37,7 @@ properties: + type: uint64 + description: > + Time interval to arm the watchdog, in milli-second. +- default: 30000 ++ default: 600000 + - name: TimeRemaining + type: uint64 + description: > +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch new file mode 100644 index 000000000..9052435ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch @@ -0,0 +1,34 @@ +From eeac4cf4528994aeb213d549daf4c033ac9d3bbc Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Fri, 24 Aug 2018 17:55:35 +0800 +Subject: [PATCH] Add RestoreDelay interface for power restore delay + +Which provide one property "PowerRestoreDelay" + +Change-Id: I4e6d3e45948b1e288301b4aa52cc08cace4f1bc2 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../Control/Power/RestoreDelay.interface.yaml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + create mode 100644 xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml + +diff --git a/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml b/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +new file mode 100644 +index 0000000..55ee80a +--- /dev/null ++++ b/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +@@ -0,0 +1,11 @@ ++description: > ++ Implement to specify power transition behavior on a BMC reset. ++ The implementation based on restore policy and set a delay time ++ for power restore. ++ ++properties: ++ - name: PowerRestoreDelay ++ type: uint16 ++ description: > ++ The delay time for power restore. ++ Power Restore Delay is NOT applied on power policy is "Always Off" +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch new file mode 100644 index 000000000..9471c7ab2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch @@ -0,0 +1,86 @@ +From 7260c24b201759f3a5168eebfee215072c13e641 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Thu, 30 Aug 2018 16:22:43 +0800 +Subject: [PATCH] Add ErrConfig.yaml interface for processor error + configuration. + +Which provide 3 properties: + ResetCfg + type: byte + description: > + Reset Configuration + [0]: CATERR Reset Enabled + 0b: Disabled + 1b: Enabled + [1]: ERR2 Reset Enabled + 0b: Disabled + 1b: Enabled + [7:2]: Reserved + ResetErrorOccurrenceCounts + type: byte + description: > + Reset Error Occurrence Counts + [0]: Reset CPU Error Counts + 0b: Keep CPU Error Counts + 1b: Reset all CPU Error Counts to zero + [7:1]: Reserved + CATERRStatus + type: array[byte] + description: > + For all CPUs including the non-legacy socket CPU + CPU CATERR (Core Error) occurrence + [5:0]: Error Occurrence Count + [7:6]: CPU Status + 00b: Disabled + 01b: Enabled + 11b: Not Present + +Change-Id: Ibc5a7a5e15c998e56c04e23b1043d99243a91171 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../Processor/ErrConfig.interface.yaml | 33 +++++++++++++++++++ + 1 file changed, 33 insertions(+) + create mode 100644 xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml + +diff --git a/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml b/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +new file mode 100644 +index 0000000..2304263 +--- /dev/null ++++ b/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +@@ -0,0 +1,33 @@ ++description: > ++ This defines processor error configuration. ++properties: ++ - name: ResetCfg ++ type: byte ++ description: > ++ Reset Configuration ++ [0]: CATERR Reset Enabled ++ 0b: Disabled ++ 1b: Enabled ++ [1]: ERR2 Reset Enabled ++ 0b: Disabled ++ 1b: Enabled ++ [7:2]: Reserved ++ ++ - name: ResetErrorOccurrenceCounts ++ type: byte ++ description: > ++ Reset Error Occurrence Counts ++ [0]: Reset CPU Error Counts ++ 0b: Keep CPU Error Counts ++ 1b: Reset all CPU Error Counts to zero ++ [7:1]: Reserved ++ - name: CATERRStatus ++ type: array[byte] ++ description: > ++ For all CPUs including the non-legacy socket CPU ++ CPU CATERR (Core Error) occurrence ++ [5:0]: Error Occurrence Count ++ [7:6]: CPU Status ++ 00b: Disabled ++ 01b: Enabled ++ 11b: Not Present +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0014-Add-multiple-state-signal-for-host-start-and-stop.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0014-Add-multiple-state-signal-for-host-start-and-stop.patch new file mode 100644 index 000000000..a8d732dab --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0014-Add-multiple-state-signal-for-host-start-and-stop.patch @@ -0,0 +1,63 @@ +From 6d0069f4a2b4637c58fd321c9db3034ac9dd17c7 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 12 Sep 2018 15:03:26 +0800 +Subject: [PATCH] Add multiple state signal for host start and stop + +Add six host state signals like following: + - name: PreHostStart + description: > + This is the signal to indicate host is at pre start stage. + - name: PostHostStart + description: > + This is the signal to indicate host is at start complete stage. + - name: HostStarting + description: > + This is the signal to indicate host is at starting stage. + - name: HostStoping + description: > + This is the signal to indicate host is at stoping stage. + - name: PreHostStop + description: > + This is the signal to indicate host is at pre stop stage. + - name: PostHostStop + description: > + This is the signal to indicate host is at stop complete stage. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + xyz/openbmc_project/State/Host.interface.yaml | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/State/Host.interface.yaml b/xyz/openbmc_project/State/Host.interface.yaml +index 8531a27..1ceba13 100644 +--- a/xyz/openbmc_project/State/Host.interface.yaml ++++ b/xyz/openbmc_project/State/Host.interface.yaml +@@ -96,4 +96,23 @@ enumerations: + - name: RTCWakeup + description: > + System start/restart detected and caused by system real time +- clock(RTC) wakeup. +\ No newline at end of file ++ clock(RTC) wakeup. ++signals: ++ - name: PreHostStart ++ description: > ++ This is the signal to indicate host is at pre start stage. ++ - name: PostHostStart ++ description: > ++ This is the signal to indicate host is at start complete stage. ++ - name: HostStarting ++ description: > ++ This is the signal to indicate host is at starting stage. ++ - name: HostStoping ++ description: > ++ This is the signal to indicate host is at stoping stage. ++ - name: PreHostStop ++ description: > ++ This is the signal to indicate host is at pre stop stage. ++ - name: PostHostStop ++ description: > ++ This is the signal to indicate host is at stop complete stage. +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch new file mode 100644 index 000000000..576bae81a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch @@ -0,0 +1,227 @@ +From 9490574667485cd407193ff9f0d6a96f8c2c87d3 Mon Sep 17 00:00:00 2001 +From: cyang29 <cheng.c.yang@intel.com> +Date: Wed, 12 Sep 2018 00:27:23 +0800 +Subject: [PATCH] Add DBUS interface of SMBIOS MDR V2 + +Support: + SMBIOS MDR V2 service interface. + SMBIOS MDR V2 IPMI Command + SMBIOS MDR V2 Redfish interface. +Base on SMBIOS spec DSP0134_3.0.0 and Managed Data Region +Specification Revision 4 +--- + xyz/openbmc_project/Smbios/MDR_V2.errors.yaml | 9 + + xyz/openbmc_project/Smbios/MDR_V2.interface.yaml | 158 +++++++++++++++++++++++ + xyz/openbmc_project/Smbios/README.md | 21 +++ + 3 files changed, 188 insertions(+) + create mode 100644 xyz/openbmc_project/Smbios/MDR_V2.errors.yaml + create mode 100644 xyz/openbmc_project/Smbios/MDR_V2.interface.yaml + create mode 100644 xyz/openbmc_project/Smbios/README.md + +diff --git a/xyz/openbmc_project/Smbios/MDR_V2.errors.yaml b/xyz/openbmc_project/Smbios/MDR_V2.errors.yaml +new file mode 100644 +index 0000000..88bd6db +--- /dev/null ++++ b/xyz/openbmc_project/Smbios/MDR_V2.errors.yaml +@@ -0,0 +1,9 @@ ++- name: InvalidParameter ++ description: > ++ An invalid parameter is attempted. ++- name: UpdateInProgress ++ description: > ++ Update is in progress. ++- name: InvalidId ++ description: > ++ An invalid Id is attempted. +diff --git a/xyz/openbmc_project/Smbios/MDR_V2.interface.yaml b/xyz/openbmc_project/Smbios/MDR_V2.interface.yaml +new file mode 100644 +index 0000000..f97700a +--- /dev/null ++++ b/xyz/openbmc_project/Smbios/MDR_V2.interface.yaml +@@ -0,0 +1,158 @@ ++description: > ++ SMBIOS MDR V2 service ++methods: ++ - name: GetDirectoryInformation ++ description: > ++ Get the directory with directory index. ++ parameters: ++ - name: dirIndex ++ type: byte ++ description: > ++ Directory index of SMBIOS. ++ returns: ++ - name: dir ++ type: array[byte] ++ description: > ++ Directory of agent. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: GetDataInformation ++ description: > ++ Get the data info with id index and data set ID. ++ parameters: ++ - name: idIndex ++ type: byte ++ description: > ++ Index of SMBIOS directory. ++ returns: ++ - name: dataInfo ++ type: array[byte] ++ description: > ++ Data information of SMBIOS. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: SendDirectoryInformation ++ description: > ++ Send directory information to SMBIOS directory. ++ parameters: ++ - name: dirVersion ++ type: byte ++ description: > ++ A counter which increments each time directory updated. ++ - name: dirIndex ++ type: byte ++ description: > ++ Directory index of SMBIOS. ++ - name: returnedEntries ++ type: byte ++ description: > ++ Indicates number of directory entries. ++ - name: remainingEntries ++ type: byte ++ description: > ++ Remaining entries which are higher than index in this transfer. ++ - name: dirEntry ++ type: array[byte] ++ description: > ++ Data set ID of SMBIOS table. ++ returns: ++ - name: status ++ type: boolean ++ description: > ++ Need to continue directory transmisson or not. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: GetDataOffer ++ description: > ++ Get data set ID. ++ returns: ++ - name: offer ++ type: array[byte] ++ description: > ++ Data set ID. ++ errors: ++ - self.Error.UpdateInProgress ++ ++ - name: SendDataInformation ++ description: > ++ Send data information with directory index. ++ parameters: ++ - name: idIndex ++ type: byte ++ description: > ++ Index of SMBIOS directory. ++ - name: flag ++ type: byte ++ description: > ++ Valid flag to set dir entry status. ++ - name: dataLen ++ type: uint32 ++ description: > ++ The length of the data in bytes. ++ - name: dataVer ++ type: uint32 ++ description: > ++ The version number of this data. ++ - name: timeStamp ++ type: uint32 ++ description: > ++ Timestamp determinded by the agent. ++ returns: ++ - name: status ++ type: boolean ++ description: > ++ Whether data changes. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: FindIdIndex ++ description: > ++ Find id index by data info. ++ parameters: ++ - name: dataInfo ++ type: array[byte] ++ description: > ++ Data info of data entry. ++ returns: ++ - name: idIndex ++ type: int32 ++ description: > ++ Id index of data entry. ++ errors: ++ - self.Error.InvalidId ++ ++ - name: AgentSynchronizeData ++ description: > ++ Synchronize SMBIOS data from file. ++ returns: ++ - name: status ++ type: boolean ++ description: > ++ Whether synchronization succeed or not. ++ ++ - name: SynchronizeDirectoryCommonData ++ description: > ++ Synchronize directory common data. ++ parameters: ++ - name: idIndex ++ type: byte ++ description: > ++ Index of SMBIOS directory. ++ - name: size ++ type: uint32 ++ description: > ++ Size of data that BIOS prepare to transfer. ++ returns: ++ - name: commonData ++ type: array[uint32] ++ description: > ++ Directory common data includes data size, version and timestamp. ++ ++properties: ++ - name: DirectoryEntries ++ type: byte ++ description: > ++ Numbers of directory entries. +diff --git a/xyz/openbmc_project/Smbios/README.md b/xyz/openbmc_project/Smbios/README.md +new file mode 100644 +index 0000000..415ac52 +--- /dev/null ++++ b/xyz/openbmc_project/Smbios/README.md +@@ -0,0 +1,22 @@ ++# SMBIOS MDR V2 ++ ++## Overview ++SMBIOS MDR V2 service exposes D-Bus methods for SMBIOS Version 2 operations. ++ ++### SMBIOS MDR V2 Interface ++SMBIOS MDR V2 interface `xyz.openbmc_project.Smbios.MDR_V2` provides following ++methods. ++#### methods ++* GetDirectoryInformation - Get the directory with directory index. ++* GetDataInformation - Get the data information with id index and data set ID. ++* SendDirectoryInformation - Send directory information to SMBIOS directory. ++* GetDataOffer - Get data set ID. ++* SendDataInformation - Send data information with directory index. ++* FindIdIndex - Find id index by data info. ++* SynchronizeDirectoryCommonData - Synchronize directory common data before ++SMBIOS data start to transfer. ++* AgentSynchronizeData - Synchronize SMBIOS data from file after data transfer ++complete. ++ ++#### properties ++* DirEntries - Numbers of directory entries. Default: 0 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch new file mode 100644 index 000000000..587bcebf1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch @@ -0,0 +1,42 @@ +From 7ebb72a93922a0163a5b35c277f3bbd241bdf78c Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 22 Oct 2018 16:20:36 +0800 +Subject: [PATCH] Add shutdown policy interface for get/set shutdown policy OEM + IPMI commands + +The policy property is used to store the shutdown policy. + +Tested by: +busctl get-property "xyz.openbmc_project.Settings" \ +"/xyz/openbmc_project/control/shutdown_policy_config" \ +"xyz.openbmc_project.Control.ShutdownPolicy" "Policy" + +busctl set-property "xyz.openbmc_project.Settings" \ +"/xyz/openbmc_project/control/shutdown_policy_config" \ +"xyz.openbmc_project.Control.ShutdownPolicy" "Policy" y 1 + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + create mode 100644 xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml + +diff --git a/xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml b/xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml +new file mode 100644 +index 0000000..e562ea8 +--- /dev/null ++++ b/xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml +@@ -0,0 +1,10 @@ ++description: > ++ An interface for node shutdown policy on multi-node products. ++properties: ++ - name: Policy ++ type: byte ++ description: > ++ 0: Do not shutdown node on a power supply over current(OC) ++ or a power supply over temperature(OT) event. ++ 1: Shutdown node on an OC/OT event. ++ Only available on multi-node products. +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0018-Define-post-code-interfaces-for-post-code-manager.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0018-Define-post-code-interfaces-for-post-code-manager.patch new file mode 100644 index 000000000..ce23c222b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0018-Define-post-code-interfaces-for-post-code-manager.patch @@ -0,0 +1,64 @@ +From f88cac8364d5312e29208018909827d2da4a0f87 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 19 Feb 2019 16:52:51 +0800 +Subject: [PATCH] Define post code interfaces for post code manager + +It includes one method and 2 properties. +properties: + - name: CurrentBootCycleIndex + description: > + It is used to indicate current boot cycle index. + - name: MaxBootCycleNum + description: > + The max cached boot cycles for post code. +methods: + - name: GetPostCodes + description: > + Method to get the cached post code for each boot cycle. +TestBy: bitbake build + +Signeoff-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../State/Boot/PostCode.interface.yaml | 30 ++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + create mode 100644 xyz/openbmc_project/State/Boot/PostCode.interface.yaml + +diff --git a/xyz/openbmc_project/State/Boot/PostCode.interface.yaml b/xyz/openbmc_project/State/Boot/PostCode.interface.yaml +new file mode 100644 +index 0000000..711749d +--- /dev/null ++++ b/xyz/openbmc_project/State/Boot/PostCode.interface.yaml +@@ -0,0 +1,30 @@ ++description: > ++ Monitor Post code coming and buffer all of them based on boot cycle ++ into file system. ++ ++properties: ++ - name: CurrentBootCycleIndex ++ type: uint16 ++ description: > ++ It is used to indicate current boot cycle index. ++ - name: MaxBootCycleNum ++ type: uint16 ++ description: > ++ The max cached boot cycles for post code. ++methods: ++ - name: GetPostCodes ++ description: > ++ Method to get the cached post code for each boot cycle. ++ parameters: ++ - name: Index ++ type: uint16 ++ description: > ++ Index indicates which boot cycle of post codes is requested. ++ returns: ++ - name: codes ++ type: array[uint64] ++ description: > ++ An array of post codes of one boot cycle. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - xyz.openbmc_project.Common.Error.InvalidArgument +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0019-Creating-the-Session-interface-for-Host-and-LAN.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0019-Creating-the-Session-interface-for-Host-and-LAN.patch new file mode 100644 index 000000000..e1208cf5b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0019-Creating-the-Session-interface-for-Host-and-LAN.patch @@ -0,0 +1,126 @@ +From f805522921d69eb4ea70248fe7e54557363aacea Mon Sep 17 00:00:00 2001 +From: Suryakanth Sekar <suryakanth.sekar@linux.intel.com> +Date: Sat, 2 Mar 2019 03:31:09 +0530 +Subject: [PATCH] Creating the Session interface- for Host and LAN + +Defined all needed session properites to manage the session in +host and also in LAN + +Change-Id: Ic7cccfc3333a602ee07fcd60077ec58fd8f06304 +Signed-off-by: Suryakanth Sekar <suryakanth.sekar@linux.intel.com> +--- + xyz/openbmc_project/Session/Info.interface.yaml | 56 +++++++++++++++++++++++++ + xyz/openbmc_project/Session/README.md | 38 +++++++++++++++++ + 2 files changed, 94 insertions(+) + create mode 100644 xyz/openbmc_project/Session/Info.interface.yaml + create mode 100644 xyz/openbmc_project/Session/README.md + +diff --git a/xyz/openbmc_project/Session/Info.interface.yaml b/xyz/openbmc_project/Session/Info.interface.yaml +new file mode 100644 +index 0000000..fbb5a45 +--- /dev/null ++++ b/xyz/openbmc_project/Session/Info.interface.yaml +@@ -0,0 +1,56 @@ ++description: > ++ Provides properties for session objects. ++ As communication to this service is done through authenticated ++ & authorized session, there won't be any validation for the both. ++ ++properties: ++ - name: SessionHandle ++ type: byte ++ description: > ++ Session Handle,unique number to locate the session. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: ChannelNum ++ type: byte ++ description: > ++ channelnumber which session is created. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: CurrentPrivilege ++ type: byte ++ description: > ++ Privilege of the session. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: RemoteIPAddr ++ type: uint32 ++ description: > ++ Remote IP address. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: RemotePort ++ type: uint16 ++ description: > ++ Remote IP Port. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: RemoteMACAddress ++ type: array[byte] ++ description: > ++ Remote MAC address. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: UserID ++ type: byte ++ description: > ++ Session created by given user ID. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: State ++ type: byte ++ description: > ++ Session state. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ ++# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +diff --git a/xyz/openbmc_project/Session/README.md b/xyz/openbmc_project/Session/README.md +new file mode 100644 +index 0000000..f220885 +--- /dev/null ++++ b/xyz/openbmc_project/Session/README.md +@@ -0,0 +1,38 @@ ++# Session Management ++ ++## Overview ++Session Manager service exposes D-Bus methods for session management operations. ++ ++### Session Manager Interface ++Session manager interface `xyz.openbmc_project.Session.Manager` provides following ++methods, properties and signals. ++ ++#### xyz.openbmc_project.Session.Manager interface ++##### methods ++* CreateSession - To create new session object to the system. ++ ++##### properties ++* MaxSessionSupported - To list all the groups supported in the system. ++* ActiveSessionCount - To hold the no of active sessions. ++ ++Session manager service will create session objects for every session ++in the system under object path `/xyz/openbmc_project/session/<SessionIndex>`. ++Each session object can be handled through 'org.freedesktop.DBus.ObjectManager'. ++session object will expose following properties and methods. ++ ++#### xyz.openbmc_project.Session.Info interface ++##### properties ++* SessionID - Session ID,random unique number to locate the session. ++* Channel - Session created Channel. ++* SessionPrivilege - Privilege of the session. ++* RemoteIPAddr – Remote IP address. ++* RemotePort - Remote Port address. ++* RemoteMACAddress -Remote MAC Address. ++* UserID - Session created by given user id. ++ ++ ++ ++#### xyz.openbmc_project.Object.Delete ++#### methods ++* Delete - To delete the session object in the system. ++ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0020-Create-dbus-interface-for-SOL-commands.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0020-Create-dbus-interface-for-SOL-commands.patch new file mode 100644 index 000000000..68d2c92b7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0020-Create-dbus-interface-for-SOL-commands.patch @@ -0,0 +1,76 @@ +From 2820ca36ab21c52341cdbde477756f960eaeb68b Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Wed, 13 Mar 2019 11:19:07 +0800 +Subject: [PATCH] Create dbus interface for SOL commands + +Create dbus properties for Set/Get SOL config parameter command. +Some platforms need to call Set/Get SOL config parameter command +through KCS, and since sol manager in net-ipmid cannot be accessed +by commands in host-ipmid, need to create a dbus interface in +phospher-settings to transfer properties from host-ipmid to +net-ipmid. + +TestedBy: +With the related code change in net-ipmid, busctl introspect +xyz.openbmc_project.Ipmi.SOL /xyz/openbmc_project/SOL +can show all the properties needed. + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +--- + xyz/openbmc_project/Ipmi/SOL.interface.yaml | 44 ++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + create mode 100644 xyz/openbmc_project/Ipmi/SOL.interface.yaml + +diff --git a/xyz/openbmc_project/Ipmi/SOL.interface.yaml b/xyz/openbmc_project/Ipmi/SOL.interface.yaml +new file mode 100644 +index 0000000..94db59f +--- /dev/null ++++ b/xyz/openbmc_project/Ipmi/SOL.interface.yaml +@@ -0,0 +1,44 @@ ++description: > ++ SOL properties use for Get/Set SOL config parameter command in host-ipmid ++ sending config to SOL process in net-ipmid. ++ Since some platforms need to access Get/Set SOL config parameter command ++ through KCS, and current sol manager is implemented in net-ipmid and ++ cannot be accessed by host-ipmid, add a dbus interface for host-ipmid ++ command to transfer properties to net-ipmid. ++ This interface will be implemented in phosphor-settings. ++properties: ++ - name: Progress ++ type: byte ++ description: > ++ Set In Progress property, indicate when any parameters are being ++ updated. ++ - name: Enable ++ type: boolean ++ description: > ++ SOL Enable property, this controls whether the SOL payload type ++ can be activated. ++ - name: Authentication ++ type: byte ++ description: > ++ If SOL enable Force Payload Encryption and Authenticaton. ++ And the minimun operating privilege level SOL required. ++ - name: AccumulateIntervalMS ++ type: byte ++ description: > ++ Character Accumulate Interval in 5ms increments. ++ BMC will wait this time before transmitting a packet. ++ - name: Threshold ++ type: byte ++ description: > ++ BMC will automatically send an SOL character data packet containing ++ this number of characters as soon as this number of characters ++ (or greater) has been accepted from the baseboard serial controller. ++ - name: RetryCount ++ type: byte ++ description: > ++ Packet will be dropped if no ACK/NACK received by time retries ++ expire. ++ - name: RetryIntervalMS ++ type: byte ++ description: > ++ Retry Interval in 10ms increments. +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend new file mode 100644 index 000000000..a2eb6649c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend @@ -0,0 +1,20 @@ +SRC_URI = "git://github.com/openbmc/phosphor-dbus-interfaces.git" +SRCREV = "5515c34fded4c217e3cc07f551d59d34cd3329c5" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0002-Modify-Dbus-for-IPv6.patch \ + file://0003-Chassis-Power-Control-are-implemented.patch \ + file://0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch \ + file://0006-dbus-interface-add-boot-option-support-for-floppy-an.patch \ + file://0007-ipmi-set-BIOS-id.patch \ + file://0009-Add-host-restart-cause-property.patch \ + file://0010-Increase-the-default-watchdog-timeout-value.patch \ + file://0012-Add-RestoreDelay-interface-for-power-restore-delay.patch \ + file://0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch \ + file://0014-Add-multiple-state-signal-for-host-start-and-stop.patch \ + file://0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch \ + file://0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch \ + file://0018-Define-post-code-interfaces-for-post-code-manager.patch \ + file://0019-Creating-the-Session-interface-for-Host-and-LAN.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service new file mode 100644 index 000000000..9af9af254 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service @@ -0,0 +1,20 @@ +[Unit] +Description=Phosphor DBus Service Discovery Manager +Before=obmc-mapper.target +After=dbus.socket + +[Service] +Restart=always +Type=dbus +ExecStart=/usr/bin/env mapperx \ + --service-namespaces="xyz. com. org." \ + --interface-namespaces="org. com. xyz." \ + --service-blacklists="org.freedesktop.systemd1" +SyslogIdentifier=phosphor-mapper +BusName={BUSNAME} +TimeoutStartSec=300 +RestartSec=5 +EnvironmentFile={envfiledir}/obmc/mapper + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend new file mode 100644 index 000000000..72d991c7e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service new file mode 100644 index 000000000..f4ffa17a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service @@ -0,0 +1,12 @@ +[Unit] +Description=Phosphor-Pid-Control Margin-based Fan Control Daemon + +[Service] +Restart=always +ExecStart={sbindir}/swampd +RestartSec=5 +StartLimitInterval=0 +Type=simple + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend new file mode 100644 index 000000000..0c56a8fe5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +inherit obmc-phosphor-systemd +SYSTEMD_SERVICE_${PN} = "phosphor-pid-control.service" +EXTRA_OECONF = "--enable-configure-dbus=yes" + +SRC_URI = "git://github.com/openbmc/phosphor-pid-control.git" +SRCREV = "90e9dbcae6c1df3127dd3de41f9d1e1b5a438828" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service new file mode 100644 index 000000000..d51fee312 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service @@ -0,0 +1,8 @@ +[Unit]
+Description=Flash BMC with fwupd script : %I
+
+[Service]
+Type=oneshot
+RemainAfterExit=no
+ExecStart=/usr/bin/fwupd.sh file:////tmp/images/%i/image-runtime
+SyslogIdentifier=fwupd
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend new file mode 100644 index 000000000..80c5ea9d3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend @@ -0,0 +1,11 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +EXTRA_OECONF += "--enable-fwupd_script" + +SYSTEMD_SERVICE_${PN}-updater += "fwupd@.service" + +SRC_URI_remove = "git://github.com/openbmc/phosphor-bmc-code-mgmt" +SRC_URI += "git://git@github.com/Intel-BMC/phosphor-bmc-code-mgmt;protocol=ssh" +SRCREV = "f8f76c29dbe2806a6eacd15847563cdf7f7567f4" + +#Currently enforcing image signature validation only for PFR images +PACKAGECONFIG_append = "${@bb.utils.contains('IMAGE_TYPE', 'pfr', ' verify_signature', '', d)}" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/gpiodaemon/gpiodaemon.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/gpiodaemon/gpiodaemon.bb new file mode 100644 index 000000000..c04069fb9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/gpiodaemon/gpiodaemon.bb @@ -0,0 +1,18 @@ +SUMMARY = "Gpio daemon service for handling gpio operations" +DESCRIPTION = "Daemon allows to block gpio access under certain conditions" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git/gpiodaemon" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" + +SRCREV = "f4d4bfc3296cb27feb17aa5d1d93b3061b56ce10" + +inherit cmake systemd +SYSTEMD_SERVICE_${PN} = "gpiodaemon.service" + +DEPENDS = "boost systemd sdbusplus phosphor-logging" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend new file mode 100644 index 000000000..5326680f6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend @@ -0,0 +1,6 @@ +SYSTEMD_LINK_${PN}_remove += "../op-start-host@.service:obmc-host-startmin@0.target.requires/op-start-host@0.service" +SYSTEMD_LINK_${PN}_remove += "../op-init-pnor@.service:obmc-host-startmin@0.target.requires/op-init-pnor@0.service" + +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires" +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires/op-start-host@0.service" +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires/op-init-pnor@0.service"
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket new file mode 100644 index 000000000..8782e4dd3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket @@ -0,0 +1,9 @@ +[Unit] +Description=BMC Webserver socket + +[Socket] +ListenStream=443 +ReusePort=true + +[Install] +WantedBy=sockets.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend new file mode 100644 index 000000000..fe09e6da2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -0,0 +1,17 @@ +SRC_URI = "git://github.com/openbmc/bmcweb.git" +SRCREV = "b01bf2991955ef267ce2be8e7a18eac984990de8" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +# add a user called bmcweb for the server to assume +# bmcweb is part of group shadow for non-root pam authentication +USERADD_PARAM_${PN} = "-r -s /usr/sbin/nologin -d /home/bmcweb -m -G shadow bmcweb" + +GROUPADD_PARAM_${PN} = "web; redfish " + +# Enable CPU Log and Raw PECI support +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_CPU_LOG=ON" +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_RAW_PECI=ON" + +# Enable Redfish BMC Journal support +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_BMC_JOURNAL=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend new file mode 100644 index 000000000..616fb9a75 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend @@ -0,0 +1,21 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI += " file://dev_id.json \ + file://channel_access.json \ + file://channel_config.json \ + file://master_write_read_white_list.json \ + " + +FILES_${PN} += " \ + ${datadir}/ipmi-providers/channel_access.json \ + ${datadir}/ipmi-providers/channel_config.json \ + ${datadir}/ipmi-providers/master_write_read_white_list.json \ + " + +do_install_append() { + install -m 0644 -D ${WORKDIR}/channel_access.json \ + ${D}${datadir}/ipmi-providers/channel_access.json + install -m 0644 -D ${WORKDIR}/channel_config.json \ + ${D}${datadir}/ipmi-providers/channel_config.json + install -m 0644 -D ${WORKDIR}/master_write_read_white_list.json \ + ${D}${datadir}/ipmi-providers/master_write_read_white_list.json +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json new file mode 100644 index 000000000..299483121 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json @@ -0,0 +1,23 @@ +{ + "1" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + }, + "2" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + }, + "3" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json new file mode 100644 index 000000000..13b945fd0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json @@ -0,0 +1,178 @@ +{ + "0" : { + "name" : "IPMB", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "1" : { + "name" : "eth1", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "2" : { + "name" : "eth2", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "3" : { + "name" : "eth0", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "4" : { + "name" : "EMP", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "5" : { + "name" : "ICMB", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "6" : { + "name" : "SMLINK", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "7" : { + "name" : "SMM", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "8" : { + "name" : "INTRABMC", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "9" : { + "name" : "SIPMB", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "10" : { + "name" : "PCIE", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "11" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "12" : { + "name" : "INTERNAL", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "13" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "14" : { + "name" : "SELF", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "15" : { + "name" : "SMS", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json new file mode 100644 index 000000000..e561569d9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json @@ -0,0 +1,2 @@ +{"id": 35, "revision": 0, "addn_dev_support": 191, + "manuf_id": 343, "prod_id": 123, "aux": 0} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json new file mode 100644 index 000000000..9fdb3c916 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json @@ -0,0 +1,49 @@ +{ + "filters": [ + { + "busId": "0x01", + "slaveAddr": "0x4d", + "command": "0x00" + }, + { + "busId": "0x01", + "slaveAddr": "0x57", + "command": "0x00" + }, + { + "busId": "0x02", + "slaveAddr": "0x40", + "command": "0x00" + }, + { + "busId": "0x02", + "slaveAddr": "0x49", + "command": "0x00" + }, + { + "busId": "0x02", + "slaveAddr": "0x51", + "command": "0x00" + }, + { + "busId": "0x03", + "slaveAddr": "0x44", + "command": "0x00" + }, + { + "busId": "0x03", + "slaveAddr": "0x68", + "command": "0x00" + }, + { + "busId": "0x06", + "slaveAddr": "0x40", + "command": "0x00" + }, + { + "busId": "0x07", + "slaveAddr": "0x51", + "command": "0x00" + } + ] +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend new file mode 100644 index 000000000..2d892ad1a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI += " file://config.yaml" + +#override source file before it is used for final FRU file (merged from multiple sources) +do_install() { + cp ${WORKDIR}/config.yaml ${config_datadir}/ +} + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml new file mode 100644 index 000000000..e9b7a621e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml @@ -0,0 +1,31 @@ +# A YAML similar to this example would have to be generated, for eg with MRW +# inputs and system configuration, to depict IPMI Fru information. +# +# This file maps IPMI properties to phosphor dbus inventory properties +# +# This YAML could help generate C++ code. +# Format of the YAML: +# Fruid: +# Associated Fru paths +# d-bus Interfaces +# d-bus Properties +# IPMI Fru mapping +0: + /system/board/WFP_Baseboard: + entityID: 23 + entityInstance: 1 + interfaces: + xyz.openbmc_project.Inventory.Item: + name: + IPMIFruProperty: Product Name + IPMIFruSection: Product + xyz.openbmc_project.Inventory.Decorator.Asset: + Manufacturer: + IPMIFruProperty: Manufacturer + IPMIFruSection: Product + PartNumber: + IPMIFruProperty: Part Number + IPMIFruSection: Product + SerialNumber: + IPMIFruProperty: Serial Number + IPMIFruSection: Product diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Modify-dbus-interface-for-power-control.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Modify-dbus-interface-for-power-control.patch new file mode 100644 index 000000000..236bd18f4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Modify-dbus-interface-for-power-control.patch @@ -0,0 +1,31 @@ +From 39df500f277eca01d6a0538d4db8ec34894d9441 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 12:59:12 +0800 +Subject: [PATCH] Modify dbus interface for power control + +Switch power control service namespace from "org" to "xyz", +to compatible with new intel-chassis services + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 6002e7a..0e83bba 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -786,8 +786,8 @@ ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_data_len_t data_len, + ipmi_context_t context) + { +- const char* objname = "/org/openbmc/control/power0"; +- const char* intf = "org.openbmc.control.Power"; ++ const char* objname = "/xyz/openbmc_project/Chassis/Control/Power0"; ++ const char* intf = "xyz.openbmc_project.Chassis.Control.Power"; + + sd_bus* bus = NULL; + sd_bus_message* reply = NULL; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Modify-dbus-interface-for-chassis-control.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Modify-dbus-interface-for-chassis-control.patch new file mode 100644 index 000000000..9061481ac --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Modify-dbus-interface-for-chassis-control.patch @@ -0,0 +1,33 @@ +From 48ac37551cd51415deafe8b1dcb23ebeef1e8ade Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 13:04:42 +0800 +Subject: [PATCH] Modify-dbus-interface-for-chassis-control + +Switch chassis control service namespace from "org" to "xyz", +to compatible with new intel-chassis services + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index b089331..f2889c5 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -393,9 +393,9 @@ ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_data_len_t data_len, + ipmi_context_t context) + { +- const char* objname = "/org/openbmc/control/chassis0"; ++ const char* objname = "/xyz/openbmc_project/Chassis/Control/Chassis"; + const char* iface = "org.freedesktop.DBus.Properties"; +- const char* chassis_iface = "org.openbmc.control.Chassis"; ++ const char* chassis_iface = "xyz.openbmc_project.Chassis.Control.Chassis"; + sd_bus_message* reply = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + int r = 0; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0009-IPv6-Network-changes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0009-IPv6-Network-changes.patch new file mode 100644 index 000000000..3d9179ce5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0009-IPv6-Network-changes.patch @@ -0,0 +1,909 @@ +From cd4bc9e4291771f638f66efa205bf8fbec518546 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Mon, 4 Feb 2019 10:30:12 -0800 +Subject: [PATCH] IPv6 Network changes + +Allow IPv6 IPMI set/get commands + +Signed-off-by: David Cobbley <david.j.cobbley@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> + +Change-Id: If5528d3b7294c5f8c17db5919439235d0fad0446 +--- + transporthandler.cpp | 667 ++++++++++++++++++++++++++++++++++++++++++- + transporthandler.hpp | 68 +++++ + types.hpp | 9 + + utils.hpp | 1 + + 4 files changed, 744 insertions(+), 1 deletion(-) + +Index: phosphor-host-ipmid.clean/transporthandler.cpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.cpp ++++ phosphor-host-ipmid.clean/transporthandler.cpp +@@ -41,6 +41,12 @@ extern std::unique_ptr<phosphor::Timer> + + const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx + constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; ++constexpr auto ipv6Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv6"; ++ ++static const std::array<std::string, 3> ipAddressEnablesType = { ++ "xyz.openbmc_project.Network.EthernetInterface.IPAllowed.IPv4Only", ++ "xyz.openbmc_project.Network.EthernetInterface.IPAllowed.IPv6Only", ++ "xyz.openbmc_project.Network.EthernetInterface.IPAllowed.IPv4AndIPv6"}; + + std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig; + +@@ -400,7 +406,6 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + ipmi_context_t context) + { + ipmi_ret_t rc = IPMI_CC_OK; +- *data_len = 0; + + using namespace std::chrono_literals; + +@@ -414,6 +419,9 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + auto reqptr = reinterpret_cast<const set_lan_t*>(request); + sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); + ++ size_t reqLen = *data_len; ++ *data_len = 0; ++ + // channel number is the lower nibble + int channel = reqptr->channel & CHANNEL_MASK; + auto ethdevice = ipmi::network::ChanneltoEthernet(channel); +@@ -437,6 +445,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::IPSRC: + { ++ if (reqLen != LAN_PARAM_IPSRC_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + uint8_t ipsrc{}; + std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE); + channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc); +@@ -445,6 +458,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::MAC: + { ++ if (reqLen != LAN_PARAM_MAC_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + char mac[SIZE_MAC]; + + std::snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT, +@@ -465,6 +483,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::SUBNET: + { ++ if (reqLen != LAN_PARAM_SUBNET_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + std::snprintf(netmask, INET_ADDRSTRLEN, + ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], + reqptr->data[1], reqptr->data[2], reqptr->data[3]); +@@ -474,6 +497,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::GATEWAY: + { ++ if (reqLen != LAN_PARAM_GATEWAY_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + std::snprintf(gateway, INET_ADDRSTRLEN, + ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], + reqptr->data[1], reqptr->data[2], reqptr->data[3]); +@@ -483,6 +511,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::VLAN: + { ++ if (reqLen != LAN_PARAM_VLAN_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + uint16_t vlan{}; + std::memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE); + // We are not storing the enable bit +@@ -495,6 +528,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::INPROGRESS: + { ++ if (reqLen != LAN_PARAM_INPROGRESS_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + if (reqptr->data[0] == SET_COMPLETE) + { + channelConf->lan_set_in_progress = SET_COMPLETE; +@@ -523,6 +561,122 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + } + break; + ++ case LanParam::IPV6_AND_IPV4_ENABLES: ++ { ++ if (reqLen != LAN_PARAM_IPV6_AND_IPV4_ENABLES_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6AddressingEnables = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ADDRESSES: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ADDRESSES_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6AddressSource = ++ reqptr->data[1] & 0x81; // Looking at bit 0 and bit 7 ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, &reqptr->data[2], tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6Addr.assign(tmpIPV6); ++ channelConf->ipv6Prefix = reqptr->data[19]; ++ break; ++ } ++ ++ case LanParam::IPV6_ROUTER_ADDRESS_CONF_CTRL: ++ { ++ if (reqLen != LAN_PARAM_IPV6_ROUTER_ADDRESS_CONF_CTRL_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6RouterAddressConfigControl = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_1_IP_ADDR: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_1_IP_ADDR_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6GatewayAddr.assign(tmpIPV6); ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_LEN: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_LEN_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6GatewayPrefixLength = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_VAL: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_VAL_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6GatewayPrefixValue.assign(tmpIPV6); ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_2_IP_ADDR: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_2_IP_ADDR_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6BackupGatewayAddr.assign(tmpIPV6); ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_LEN: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_LEN_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6BackupGatewayPrefixLength = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_VAL: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_VAL_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6BackupGatewayPrefixValue.assign(tmpIPV6); ++ break; ++ } ++ + default: + { + rc = IPMI_CC_PARM_NOT_SUPPORTED; +@@ -549,6 +703,7 @@ ipmi_ret_t ipmi_transport_get_lan(ipmi_n + ipmi_ret_t rc = IPMI_CC_OK; + *data_len = 0; + const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0 ++ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; + + get_lan_t* reqptr = (get_lan_t*)request; + // channel number is the lower nibble +@@ -687,6 +842,489 @@ ipmi_ret_t ipmi_transport_get_lan(ipmi_n + static_cast<uint8_t>(cipherList.size()); + break; + } ++ case LanParam::IPV6_AND_IPV4_SUPPORTED: ++ { ++ uint8_t addressSupport = ++ 0x1; // Allow both IPv4 & IPv6 simultaneously ++ std::array<uint8_t, 2> buf = {current_revision, addressSupport}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_AND_IPV4_ENABLES: ++ { ++ // If DHCP, check if you have an ipv6 and ipv4 address. If static ++ // return not supported ++ ++ // 00h check if conf DHCP == ipv4 or off ++ // 01h check if conf DHCP == ipv6 ++ // 02h check if DHCP == true ++ ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ std::string networkInterfacePath; ++ uint8_t ipVAddressEnables = 0; ++ ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ try ++ { ++ ipmi::ObjectTree ancestorMap; ++ // if the system has an ip object,then ++ // get the IP object. ++ auto ipObject = ++ ipmi::getDbusObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ // Get the parent interface of the IP object. ++ try ++ { ++ ipmi::InterfaceList interfaces; ++ interfaces.emplace_back( ++ ipmi::network::ETHERNET_INTERFACE); ++ ++ ancestorMap = ipmi::getAllAncestors( ++ bus, ipObject.first, std::move(interfaces)); ++ } ++ catch (InternalFailure& e) ++ { ++ // if unable to get the parent interface ++ // then commit the error and return. ++ log<level::ERR>( ++ "Unable to get the parent interface", ++ entry("PATH=%s", ipObject.first.c_str()), ++ entry("INTERFACE=%s", ++ ipmi::network::ETHERNET_INTERFACE)); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ // for an ip object there would be single parent ++ // interface. ++ networkInterfacePath = ancestorMap.begin()->first; ++ } ++ catch (InternalFailure& e) ++ { ++ // if there is no ip configured on the system,then ++ // get the network interface object. ++ auto networkInterfaceObject = ipmi::getDbusObject( ++ bus, ipmi::network::ETHERNET_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ networkInterfacePath = networkInterfaceObject.first; ++ } ++ ++ std::string ipEnables = ++ sdbusplus::message::variant_ns::get<std::string>( ++ ipmi::getDbusProperty(bus, ipmi::network::SERVICE, ++ networkInterfacePath, ++ ipmi::network::ETHERNET_INTERFACE, ++ "IPAddressEnables")); ++ ++ // check if on off ipv4 ipv6, etc. ++ bool found = false; ++ for (uint8_t ii = 0; ii < ipAddressEnablesType.size(); ii++) ++ { ++ if (ipEnables == ipAddressEnablesType[ii]) ++ { ++ ipVAddressEnables = ii; ++ found = true; ++ break; ++ } ++ } ++ if (!found) ++ { ++ return IPMI_CC_PARM_NOT_SUPPORTED; ++ } ++ } ++ else ++ { ++ ipVAddressEnables = channelConf->ipv6AddressingEnables; ++ } ++ ++ std::array<uint8_t, 2> buf = {current_revision, ipVAddressEnables}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATUS: ++ { ++ // Number of IPV6 addresses that are supported ++ constexpr std::array<uint8_t, 3> statusData = {1, 1, 3}; ++ ++ std::array<uint8_t, 4> buf = {current_revision, statusData[0], ++ statusData[1], statusData[2]}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ADDRESSES: ++ { ++ // Only return set selector 0 ++ uint8_t ipv6SetSelector = 0; ++ std::string ipaddress; ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ uint8_t ipv6AddressSource = 0; ++ uint8_t prefixLength = 0; ++ uint8_t status = 0; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ try ++ { ++ auto ipObjectInfo = ++ ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ ++ auto properties = ipmi::getAllDbusProperties( ++ bus, ipObjectInfo.second, ipObjectInfo.first, ++ ipmi::network::IP_INTERFACE); ++ ++ std::string origin = ++ sdbusplus::message::variant_ns::get<std::string>( ++ properties["Origin"]); ++ if (sdbusplus::message::variant_ns::get<std::string>( ++ properties["Origin"]) == ++ "xyz.openbmc_project.Network.IP.AddressOrigin.Static") ++ { ++ ipaddress = ++ sdbusplus::message::variant_ns::get<std::string>( ++ properties["Address"]); ++ ipv6AddressSource = 0x81; // Looking at bit 0 and bit 7 ++ prefixLength = ++ sdbusplus::message::variant_ns::get<uint8_t>( ++ properties["PrefixLength"]); ++ status = 0; ++ } ++ } ++ // ignore the exception, as it is a valid condition that ++ // the system is not configured with any IP. ++ catch (InternalFailure& e) ++ { ++ // nothing to do. ++ } ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ ipv6AddressSource = channelConf->ipv6AddressSource; ++ ipaddress = channelConf->ipv6Addr.c_str(); ++ prefixLength = channelConf->ipv6Prefix; ++ status = 1; ++ } ++ ++ std::array<uint8_t, ipmi::network::IPV6_ADDRESS_STATUS_SIZE> buf = { ++ current_revision, ipv6SetSelector, ipv6AddressSource}; ++ inet_pton(AF_INET6, ipaddress.c_str(), ++ reinterpret_cast<void*>(&buf[3])); ++ buf[20] = prefixLength; ++ buf[21] = status; ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_STATIC_DUID_STORAGE_LENGTH: ++ { ++ // DHCP unique identified ++ // Only 1 read-only 16-byte Block needed ++ uint8_t duidLength = 1; ++ std::array<uint8_t, 2> buf = {current_revision, duidLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_STATIC_DUIDS: ++ { ++ std::string macAddress; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ auto macObjectInfo = ++ ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ auto variant = ipmi::getDbusProperty( ++ bus, macObjectInfo.second, macObjectInfo.first, ++ ipmi::network::MAC_INTERFACE, "MACAddress"); ++ ++ macAddress = ++ sdbusplus::message::variant_ns::get<std::string>(variant); ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ macAddress = channelConf->macAddress; ++ } ++ ++ std::array<uint8_t, ++ ipmi::network::IPV6_DUID_SIZE + sizeof(current_revision)> ++ buf; ++ buf = {current_revision, ++ reqptr->parameter_set, ++ reqptr->parameter_block, ++ DUID_LEN, ++ 0, // Filler byte ++ DUID_LL_TYPE, ++ 0, // Filler byte ++ DUIC_ETH_HW_TYPE}; ++ sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, ++ (&buf[8]), (&buf[9]), (&buf[10]), (&buf[11]), (&buf[12]), ++ (&buf[13])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DYNAMIC_ADDRESSES: ++ { ++ std::string ipaddress; ++ uint8_t ipv6AddressSource = 0; ++ uint8_t prefixLength = 0; ++ uint8_t status = 0; ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ try ++ { ++ auto ipObjectInfo = ++ ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ ++ auto properties = ipmi::getAllDbusProperties( ++ bus, ipObjectInfo.second, ipObjectInfo.first, ++ ipmi::network::IP_INTERFACE); ++ ++ if (sdbusplus::message::variant_ns::get<std::string>( ++ properties["Origin"]) == ++ "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") ++ { ++ ipaddress = ++ sdbusplus::message::variant_ns::get<std::string>( ++ properties["Address"]); ++ ipv6AddressSource = 0x81; // Looking at bit 0 and bit 7 ++ prefixLength = ++ sdbusplus::message::variant_ns::get<uint8_t>( ++ properties["PrefixLength"]); ++ status = 0; ++ } ++ else ++ { ++ status = 1; ++ } ++ } ++ // ignore the exception, as it is a valid condition that ++ // the system is not configured with any IP. ++ catch (InternalFailure& e) ++ { ++ // nothing to do. ++ } ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ ipaddress = channelConf->ipv6Addr; ++ ipv6AddressSource = channelConf->ipv6AddressSource; ++ prefixLength = channelConf->ipv6Prefix; ++ status = channelConf->ipv6AddressStatus; ++ } ++ ++ uint8_t ipv6SetSelector = 0; ++ std::array<uint8_t, 22> buf = {current_revision, ipv6SetSelector, ++ ipv6AddressSource}; ++ inet_pton(AF_INET6, ipaddress.c_str(), ++ reinterpret_cast<void*>(&buf[3])); ++ buf[20] = prefixLength; ++ buf[21] = status; ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_DYNAMIC_DUID_STOR_LEN: ++ { ++ uint8_t duidLength = 0; ++ // Only 1 read-only 16-byte Block needed ++ duidLength = 1; ++ ++ std::array<uint8_t, 2> buf = {current_revision, duidLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_DYNAMIC_DUIDS: ++ { ++ std::string macAddress; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ auto macObjectInfo = ++ ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ auto variant = ipmi::getDbusProperty( ++ bus, macObjectInfo.second, macObjectInfo.first, ++ ipmi::network::MAC_INTERFACE, "MACAddress"); ++ ++ macAddress = ++ sdbusplus::message::variant_ns::get<std::string>(variant); ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ macAddress = channelConf->macAddress; ++ } ++ ++ std::array<uint8_t, ++ ipmi::network::IPV6_DUID_SIZE + sizeof(current_revision)> ++ buf; ++ buf = {current_revision, ++ reqptr->parameter_set, ++ reqptr->parameter_block, ++ DUID_LEN, ++ 0, // Filler byte ++ DUID_LL_TYPE, ++ 0, // Filler byte ++ DUIC_ETH_HW_TYPE}; ++ ++ sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, ++ (&buf[8]), (&buf[9]), (&buf[10]), (&buf[11]), (&buf[12]), ++ (&buf[13])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_ROUTER_ADDRESS_CONF_CTRL: ++ { ++ // Determine if automated router discovery occurs when static ++ // addresses are used for the bmc ++ ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ std::string networkInterfacePath; ++ uint8_t dynamicRA; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ ++ try ++ { ++ ipmi::ObjectTree ancestorMap; ++ // if the system is having ip object,then ++ // get the IP object. ++ auto ipObject = ++ ipmi::getDbusObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ ++ // Get the parent interface of the IP object. ++ try ++ { ++ ipmi::InterfaceList interfaces; ++ interfaces.emplace_back( ++ ipmi::network::ETHERNET_INTERFACE); ++ ++ ancestorMap = ipmi::getAllAncestors( ++ bus, ipObject.first, std::move(interfaces)); ++ } ++ catch (InternalFailure& e) ++ { ++ // if unable to get the parent interface ++ // then commit the error and return. ++ log<level::ERR>( ++ "Unable to get the parent interface", ++ entry("PATH=%s", ipObject.first.c_str()), ++ entry("INTERFACE=%s", ++ ipmi::network::ETHERNET_INTERFACE)); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ // for an ip object there would be single parent ++ // interface. ++ networkInterfacePath = ancestorMap.begin()->first; ++ } ++ catch (InternalFailure& e) ++ { ++ // if there is no ip configured on the system,then ++ // get the network interface object. ++ auto networkInterfaceObject = ipmi::getDbusObject( ++ bus, ipmi::network::ETHERNET_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ networkInterfacePath = networkInterfaceObject.first; ++ } ++ ++ auto variant = ipmi::getDbusProperty( ++ bus, ipmi::network::SERVICE, networkInterfacePath, ++ ipmi::network::ETHERNET_INTERFACE, "IPv6AcceptRA"); ++ dynamicRA = sdbusplus::message::variant_ns::get<bool>(variant); ++ } ++ else ++ { ++ dynamicRA = channelConf->ipv6RouterAddressConfigControl; ++ } ++ ++ std::array<uint8_t, 2> buf = {current_revision, dynamicRA}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_1_IP_ADDR: ++ { ++ std::array<uint8_t, ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision}; ++ inet_pton(AF_INET6, channelConf->ipv6GatewayAddr.c_str(), ++ reinterpret_cast<void*>(&buf[1])); ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_LEN: ++ { ++ std::array<uint8_t, 2> buf = {current_revision, ++ channelConf->ipv6GatewayPrefixLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_VAL: ++ { ++ constexpr uint8_t setSelector = 0; ++ std::array<uint8_t, sizeof(setSelector) + ++ ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision, setSelector}; ++ ++ inet_pton(AF_INET6, channelConf->ipv6GatewayPrefixValue.c_str(), ++ reinterpret_cast<void*>(&buf[2])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_2_IP_ADDR: ++ { ++ std::array<uint8_t, ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision}; ++ inet_pton(AF_INET6, channelConf->ipv6BackupGatewayAddr.c_str(), ++ reinterpret_cast<void*>(&buf[1])); ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_LEN: ++ { ++ std::array<uint8_t, 2> buf = { ++ current_revision, channelConf->ipv6BackupGatewayPrefixLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_VAL: ++ { ++ ++ constexpr uint8_t setSelector = 0; ++ std::array<uint8_t, sizeof(setSelector) + ++ ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision, setSelector}; ++ inet_pton(AF_INET6, ++ channelConf->ipv6BackupGatewayPrefixValue.c_str(), ++ reinterpret_cast<void*>(&buf[2])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } + default: + log<level::ERR>("Unsupported parameter", + entry("PARAMETER=0x%x", reqptr->parameter)); +@@ -932,6 +1570,16 @@ void applyChanges(int channel) + ipaddress, prefix); + } + ++ if (!channelConf->ipv6Addr.empty() && ++ channelConf->ipv6AddressSource == ++ 0x80) // Check if IPv6 static addresses are enabled ++ { ++ ipmi::network::createIP(bus, ipmi::network::SERVICE, ++ networkInterfacePath, ipv6Protocol, ++ channelConf->ipv6Addr, ++ channelConf->ipv6Prefix); ++ } ++ + if (!gateway.empty()) + { + ipmi::setDbusProperty(bus, systemObject.second, +@@ -939,7 +1587,24 @@ void applyChanges(int channel) + ipmi::network::SYSTEMCONFIG_INTERFACE, + "DefaultGateway", std::string(gateway)); + } ++ else if (!channelConf->ipv6GatewayAddr.empty()) ++ { ++ ipmi::setDbusProperty( ++ bus, systemObject.second, systemObject.first, ++ ipmi::network::SYSTEMCONFIG_INTERFACE, "DefaultGateway", ++ std::string(channelConf->ipv6GatewayAddr)); ++ } + } ++ // set IPAddress Enables ++ ipmi::setDbusProperty( ++ bus, ipmi::network::SERVICE, networkInterfaceObject.first, ++ ipmi::network::ETHERNET_INTERFACE, "IPAddressEnables", ++ ipAddressEnablesType[channelConf->ipv6AddressingEnables]); ++ ++ ipmi::setDbusProperty( ++ bus, ipmi::network::SERVICE, networkInterfaceObject.first, ++ ipmi::network::ETHERNET_INTERFACE, "IPv6AcceptRA", ++ (bool)channelConf->ipv6RouterAddressConfigControl); + } + catch (InternalFailure& e) + { +Index: phosphor-host-ipmid.clean/transporthandler.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.hpp ++++ phosphor-host-ipmid.clean/transporthandler.hpp +@@ -80,6 +80,28 @@ enum class LanParam : uint8_t + IPV6_NEIGHBOR_TIMING_CONFIGURATION = 80, + }; + ++// Data length of parameters ++constexpr size_t LAN_PARAM_INPROGRESS_SIZE = 3; ++constexpr size_t LAN_PARAM_IP_SIZE = 6; ++constexpr size_t LAN_PARAM_IPSRC_SIZE = 3; ++constexpr size_t LAN_PARAM_MAC_SIZE = 8; ++constexpr size_t LAN_PARAM_SUBNET_SIZE = 6; ++constexpr size_t LAN_PARAM_GATEWAY_SIZE = 6; ++constexpr size_t LAN_PARAM_VLAN_SIZE = 4; ++constexpr size_t LAN_PARAM_IPV6_AND_IPV4_ENABLES_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ADDRESSES_SIZE = 23; ++constexpr size_t LAN_PARAM_IPV6_ROUTER_ADDRESS_CONF_CTRL_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_1_IP_ADDR_SIZE = 18; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_LEN_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_VAL_SIZE = 19; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_2_IP_ADDR_SIZE = 18; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_LEN_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_VAL_SIZE = 19; ++ ++constexpr uint8_t DUID_LEN = 10; ++constexpr uint8_t DUID_LL_TYPE = 3; ++constexpr uint8_t DUIC_ETH_HW_TYPE = 1; ++ + constexpr uint8_t SET_COMPLETE = 0; + constexpr uint8_t SET_IN_PROGRESS = 1; + constexpr uint8_t SET_COMMIT_WRITE = 2; // Optional +@@ -102,6 +124,20 @@ struct ChannelConfig_t + uint8_t lan_set_in_progress = SET_COMPLETE; + bool flush = false; + ++ // IPV6 parameters ++ uint8_t ipv6AddressSource = 0x0; ++ uint8_t ipv6AddressingEnables = 0x2; ++ std::string ipv6Addr; ++ uint8_t ipv6Prefix = 32; ++ uint8_t ipv6AddressStatus = 0x0; ++ uint8_t ipv6RouterAddressConfigControl = 0x0; ++ std::string ipv6GatewayAddr; ++ std::string ipv6BackupGatewayAddr; ++ uint8_t ipv6GatewayPrefixLength; ++ std::string ipv6GatewayPrefixValue; ++ uint8_t ipv6BackupGatewayPrefixLength = 0x0; ++ std::string ipv6BackupGatewayPrefixValue; ++ + void clear() + { + ipaddr.clear(); +@@ -112,6 +148,20 @@ struct ChannelConfig_t + ipsrc = ipmi::network::IPOrigin::UNSPECIFIED; + lan_set_in_progress = SET_COMPLETE; + flush = false; ++ ++ // IPv6 ++ ipv6Addr.clear(); ++ ipv6GatewayAddr.clear(); ++ ipv6BackupGatewayAddr.clear(); ++ ipv6AddressingEnables = 0x2; ++ ipv6AddressSource = 0x0; ++ ipv6Prefix = 32; ++ ipv6AddressStatus = 0x0; ++ ipv6RouterAddressConfigControl = 0x0; ++ ipv6GatewayPrefixLength = 0x0; ++ ipv6GatewayPrefixValue.clear(); ++ ipv6BackupGatewayPrefixLength = 0x0; ++ ipv6BackupGatewayPrefixValue.clear(); + } + }; + +Index: phosphor-host-ipmid.clean/types.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/types.hpp ++++ phosphor-host-ipmid.clean/types.hpp +@@ -209,6 +209,7 @@ constexpr auto ADDR_TYPE_FORMAT = "%hhx" + + constexpr auto IPV4_ADDRESS_SIZE_BYTE = 4; + constexpr auto IPV6_ADDRESS_SIZE_BYTE = 16; ++constexpr auto IPV6_ADDRESS_STATUS_SIZE = 22; + + constexpr auto DEFAULT_MAC_ADDRESS = "00:00:00:00:00:00"; + constexpr auto DEFAULT_ADDRESS = "0.0.0.0"; +@@ -220,6 +221,7 @@ constexpr auto BITS_32 = 32; + constexpr auto MASK_32_BIT = 0xFFFFFFFF; + constexpr auto VLAN_ID_MASK = 0x00000FFF; + constexpr auto VLAN_ENABLE_MASK = 0x8000; ++constexpr auto IPV6_DUID_SIZE = 18; + + enum class IPOrigin : uint8_t + { +@@ -228,5 +230,12 @@ enum class IPOrigin : uint8_t + DHCP = 2, + }; + ++enum class AddressingEnables : uint8_t ++{ ++ IPv4Only = 0, ++ IPv6Only = 1, ++ IPv4AndIPv6 = 2, ++}; ++ + } // namespace network + } // namespace ipmi +Index: phosphor-host-ipmid.clean/utils.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/utils.hpp ++++ phosphor-host-ipmid.clean/utils.hpp +@@ -246,6 +246,7 @@ namespace network + constexpr auto ROOT = "/xyz/openbmc_project/network"; + constexpr auto SERVICE = "xyz.openbmc_project.Network"; + constexpr auto IP_TYPE = "ipv4"; ++constexpr auto IPV6_TYPE = "ipv6"; + constexpr auto IPV4_PREFIX = "169.254"; + constexpr auto IPV6_PREFIX = "fe80"; + constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch new file mode 100644 index 000000000..c1ec6ac6e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch @@ -0,0 +1,56 @@ +From 4953a9f2233fd24a28da84443cea6aebecd14fbc Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 13:20:54 +0800 +Subject: [PATCH] fix "get system GUID" ipmi command + +Change-Id: I15c71607c24ad8b3e2c9065a5470002ecb1761bb +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 7 ++----- + host-ipmid-whitelist.conf | 1 + + 2 files changed, 3 insertions(+), 5 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index f2889c5..9149373 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -48,7 +48,7 @@ extern sd_bus* bus; + + constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC"; + constexpr auto bmc_state_property = "CurrentBMCState"; +-constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc"; ++// phosphor-setting-manager is the unique service that holds this interface + constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; + constexpr auto bmc_guid_property = "UUID"; + constexpr auto bmc_guid_len = 16; +@@ -546,8 +545,7 @@ ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + { + // Get the Inventory object implementing BMC interface + ipmi::DbusObjectInfo bmcObject = +- ipmi::getDbusObject(bus, bmc_interface); +- ++ ipmi::getDbusObject(bus, bmc_guid_interface); + // Read UUID property value from bmcObject + // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 + auto variant = +@@ -591,7 +589,6 @@ ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + catch (const InternalFailure& e) + { + log<level::ERR>("Failed in reading BMC UUID property", +- entry("INTERFACE=%s", bmc_interface), + entry("PROPERTY_INTERFACE=%s", bmc_guid_interface), + entry("PROPERTY=%s", bmc_guid_property)); + return IPMI_CC_UNSPECIFIED_ERROR; +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 2c37ac9..164edbe 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -40,3 +40,4 @@ + 0x2C:0x06 //<Group Extension>:<Get Asset Tag> + 0x2C:0x07 //<Group Extension>:<Get Sensor Info> + 0x2C:0x10 //<Group Extension>:<Get Temperature Readings> ++0x30:0x41 //<OEM>:<Set System GUID> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0012-ipmi-set-get-boot-options.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0012-ipmi-set-get-boot-options.patch new file mode 100644 index 000000000..243015c95 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0012-ipmi-set-get-boot-options.patch @@ -0,0 +1,64 @@ +From 7b5c6a54c049a447b1fd3a42f9d63322dcee4dc7 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 19:45:10 +0800 +Subject: [PATCH] [ipmi] set/get boot options + +1. fix issue for handling unsupported paramter +2. add support for floppy/USB boot + +Change-Id: I2b888c1ad67fec7924dd5825f78622cd216a55f4 +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 666addb..77af2dc 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -1244,7 +1244,8 @@ constexpr auto ipmiDefault = 0; + std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { + {0x01, Source::Sources::Network}, + {0x02, Source::Sources::Disk}, +- {0x05, Source::Sources::ExternalMedia}, ++ {0x05, Source::Sources::DVD}, ++ {0x0f, Source::Sources::Removable}, + {ipmiDefault, Source::Sources::Default}}; + + std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { +@@ -1255,7 +1256,8 @@ std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { + std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { + {Source::Sources::Network, 0x01}, + {Source::Sources::Disk, 0x02}, +- {Source::Sources::ExternalMedia, 0x05}, ++ {Source::Sources::DVD, 0x05}, ++ {Source::Sources::Removable, 0x0f}, + {Source::Sources::Default, ipmiDefault}}; + + std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { +@@ -1533,7 +1535,7 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + setBootMode(Mode::Modes::Regular); + } + } +- if (modeIpmiToDbus.end() != modeItr) ++ else if (modeIpmiToDbus.end() != modeItr) + { + rc = setBootMode(modeItr->second); + if (rc != IPMI_CC_OK) +@@ -1550,6 +1552,12 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + setBootSource(Source::Sources::Default); + } + } ++ else ++ { ++ // if boot option is not in support list, return error ++ *data_len = 0; ++ return IPMI_CC_INVALID_FIELD_REQUEST; ++ } + } + catch (InternalFailure& e) + { +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0013-ipmi-add-set-bios-id-to-whitelist.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0013-ipmi-add-set-bios-id-to-whitelist.patch new file mode 100644 index 000000000..ae10ab60a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0013-ipmi-add-set-bios-id-to-whitelist.patch @@ -0,0 +1,25 @@ +From ad7276f3aedb6f5aed315db57406c98f2bf71a09 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 24 Jul 2018 13:21:52 +0800 +Subject: [PATCH] [ipmi] add set bios id to whitelist + +Add "SetBIOSId" and "GetDeviceInfo" 2 OEM commands into whitelist + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + host-ipmid-whitelist.conf | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 164edbe..db54a49 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -41,3 +41,5 @@ + 0x2C:0x07 //<Group Extension>:<Get Sensor Info> + 0x2C:0x10 //<Group Extension>:<Get Temperature Readings> + 0x30:0x41 //<OEM>:<Set System GUID> ++0x30:0x26 //<OEM>:<Set BIOS ID> ++0x30:0x27 //<OEM>:<Get Device Info> +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0014-Enable-get-device-guid-ipmi-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0014-Enable-get-device-guid-ipmi-command.patch new file mode 100644 index 000000000..46dd99466 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0014-Enable-get-device-guid-ipmi-command.patch @@ -0,0 +1,45 @@ +From 482a6cc52d0ec514d6da5f4bcb04b4991f3cc36e Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 13:41:25 +0800 +Subject: [PATCH] Enable get device guid ipmi command + +The UUID interface is changed, modify the API to get the correct UUID +for device guid + +Change-Id: I0c0c7bd350992ac03f928707986a7180407d8f3f +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 937be71..89d797a 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -392,9 +392,10 @@ ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_data_len_t data_len, + ipmi_context_t context) + { +- const char* objname = "/xyz/openbmc_project/Chassis/Control/Chassis"; ++ const char* objname = ++ "/xyz/openbmc_project/inventory/system/chassis/motherboard/bmc"; + const char* iface = "org.freedesktop.DBus.Properties"; +- const char* chassis_iface = "xyz.openbmc_project.Chassis.Control.Chassis"; ++ const char* uuid_iface = "xyz.openbmc_project.Common.UUID"; + sd_bus_message* reply = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + int r = 0; +@@ -426,8 +427,9 @@ ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + entry("ERRNO=0x%X", -r)); + goto finish; + } ++ + r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply, +- "ss", chassis_iface, "uuid"); ++ "ss", uuid_iface, "UUID"); + if (r < 0) + { + log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r)); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0016-add-better-sdbusplus-exception-handling.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0016-add-better-sdbusplus-exception-handling.patch new file mode 100644 index 000000000..873eb6b16 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0016-add-better-sdbusplus-exception-handling.patch @@ -0,0 +1,153 @@ +From a445f287d4aebca68dc0321e292933311caf59ba Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 20:14:55 +0800 +Subject: [PATCH] add better sdbusplus exception handling + +Now that sdbusplus throws, we need to catch more stuff. To compound the +problem, even though sdbusplus::exception::exception inherits from +std::exception, there is a problem that prevents the code from simply +catching std::exception. + +Change-Id: I2a330e542f5d87722a4c04e6d47de2cfb2f7d7c9 +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> + +--- + apphandler.cpp | 14 +++++++-- + ipmid.cpp | 77 +++++++++++++++++++++++++++++++++++--------------- + 2 files changed, 66 insertions(+), 25 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 126de33..3cae6d5 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -312,9 +312,19 @@ ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + auto version = getActiveSoftwareVersionInfo(); + r = convert_version(version.c_str(), &rev); + } +- catch (const std::exception& e) ++ catch (sdbusplus::exception::exception& e) + { +- log<level::ERR>(e.what()); ++ log<level::ERR>("sdbusplus::exception", ++ entry("ERROR=%s", e.what())); ++ } ++ catch (std::exception& e) ++ { ++ log<level::ERR>("unexpected exception", ++ entry("ERROR=%s", e.what())); ++ } ++ catch (...) ++ { ++ log<level::ERR>("unknown exception"); + } + + if (r >= 0) +diff --git a/ipmid.cpp b/ipmid.cpp +index 2d48bfe..8d2fb37 100644 +--- a/ipmid.cpp ++++ b/ipmid.cpp +@@ -273,6 +273,10 @@ ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + } + // IPMI command handlers can throw unhandled exceptions, catch those + // and return sane error code. ++ catch (sdbusplus::exception::exception& e) ++ { ++ log<level::ERR>("sdbusplus exception", entry("EXCEPTION=%s", e.what())); ++ } + catch (const std::exception& e) + { + log<level::ERR>(e.what(), entry("NET_FUN=0x%X", netfn), +@@ -281,6 +285,23 @@ ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + *data_len = 0; + // fall through + } ++ catch (...) ++ { ++ std::exception_ptr eptr = std::current_exception(); ++ try ++ { ++ std::rethrow_exception(eptr); ++ } ++ catch (std::exception& e) ++ { ++ log<level::ERR>("unexpected uncaught exception", ++ entry("EXCEPTION=%s", e.what()), ++ entry("NET_FUN=0x%X", netfn), ++ entry("CMD=0x%X", cmd)); ++ rc = IPMI_CC_UNSPECIFIED_ERROR; ++ *data_len = 0; ++ } ++ } + // Now copy the return code that we got from handler and pack it in first + // byte. + std::memcpy(response, &rc, IPMI_CC_LEN); +@@ -361,32 +382,42 @@ final: + void cache_restricted_mode() + { + restricted_mode = false; +- using namespace sdbusplus::xyz::openbmc_project::Control::Security::server; +- using namespace internal; +- using namespace internal::cache; +- sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); +- const auto& restrictionModeSetting = +- objects->map.at(restrictionModeIntf).front(); +- auto method = dbus.new_method_call( +- objects->service(restrictionModeSetting, restrictionModeIntf).c_str(), +- restrictionModeSetting.c_str(), "org.freedesktop.DBus.Properties", +- "Get"); +- method.append(restrictionModeIntf, "RestrictionMode"); +- auto resp = dbus.call(method); +- if (resp.is_method_error()) ++ try + { +- log<level::ERR>("Error in RestrictionMode Get"); +- // Fail-safe to true. +- restricted_mode = true; +- return; ++ using namespace sdbusplus::xyz::openbmc_project::Control::Security:: ++ server; ++ using namespace internal; ++ using namespace internal::cache; ++ sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); ++ const auto& restrictionModeSetting = ++ objects->map.at(restrictionModeIntf).front(); ++ auto method = dbus.new_method_call( ++ objects->service(restrictionModeSetting, restrictionModeIntf) ++ .c_str(), ++ restrictionModeSetting.c_str(), "org.freedesktop.DBus.Properties", ++ "Get"); ++ method.append(restrictionModeIntf, "RestrictionMode"); ++ auto resp = dbus.call(method); ++ if (resp.is_method_error()) ++ { ++ log<level::ERR>("Error in RestrictionMode Get"); ++ // Fail-safe to true. ++ restricted_mode = true; ++ return; ++ } ++ sdbusplus::message::variant<std::string> result; ++ resp.read(result); ++ auto restrictionMode = RestrictionMode::convertModesFromString( ++ sdbusplus::message::variant_ns::get<std::string>(result)); ++ if (RestrictionMode::Modes::Whitelist == restrictionMode) ++ { ++ restricted_mode = true; ++ } + } +- sdbusplus::message::variant<std::string> result; +- resp.read(result); +- auto restrictionMode = RestrictionMode::convertModesFromString( +- variant_ns::get<std::string>(result)); +- if (RestrictionMode::Modes::Whitelist == restrictionMode) ++ catch (sdbusplus::exception::exception& e) + { +- restricted_mode = true; ++ // restrictionModeIntf does not exist; default to not enforcing ++ log<level::ERR>("sdbusplus exception", entry("EXCEPTION=%s", e.what())); + } + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch new file mode 100644 index 000000000..6fa69b602 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch @@ -0,0 +1,49 @@ +From 4490ee7a9fd054640af7a9da3400f76195dc2880 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 21:03:58 +0800 +Subject: [PATCH] Catch sdbusplus exceptions in IPMI net + +Missing the correct exception was causing issues with setting the IPV4 +address + +Change-Id: Ieaaacfcbaec82a0c3b110889817a7ceb9cda8d3c +Signed-off-by: Dave Cobbley <david.j.cobbley@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + transporthandler.cpp | 2 +- + utils.cpp | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/transporthandler.cpp b/transporthandler.cpp +index 6f4ec3f..6cb3feb 100644 +--- a/transporthandler.cpp ++++ b/transporthandler.cpp +@@ -1559,7 +1559,7 @@ void applyChanges(int channel) + ipmi::network::ETHERNET_INTERFACE, "IPv6AcceptRA", + (bool)channelConf->ipv6RouterAddressConfigControl); + } +- catch (InternalFailure& e) ++ catch (sdbusplus::exception::exception& e) + { + log<level::ERR>( + "Failed to set network data", entry("PREFIX=%d", prefix), +diff --git a/utils.cpp b/utils.cpp +index 225b1cc..d10b5de 100644 +--- a/utils.cpp ++++ b/utils.cpp +@@ -358,9 +358,10 @@ void deleteAllDbusObjects(sdbusplus::bus::bus& bus, + "Delete"); + } + } +- catch (InternalFailure& e) ++ catch (sdbusplus::exception::exception& e) + { +- log<level::INFO>("Unable to delete the objects having", ++ log<level::INFO>("sdbusplus exception - Unable to delete the objects", ++ entry("ERROR=%s", e.what()), + entry("INTERFACE=%s", interface.c_str()), + entry("SERVICE=%s", serviceRoot.c_str())); + } +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch new file mode 100644 index 000000000..af526c177 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch @@ -0,0 +1,143 @@ +From c14e31ebc35e0bb7b843d84683f9f2698c9c08d7 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 21:32:38 +0800 +Subject: [PATCH] Implement IPMI Commmand - Get Host Restart Cause. + +It supports to track the information about what +action last caused the system to restart. +Return value includes: restart_cause and channel_number. + +According to IPMI Spec, it includes 12 types as following: +1. Unknown 0x0 +2. IpmiCommand 0x1 +3. ResetButton 0x2 +4. PowerButton 0x3 +5. WatchdogTimer 0x4 +6. OEM 0x5 +7. PowerPolicyAlwaysOn 0x6 +8. PowerPolicyPreviousState 0x7 +9. PEF-Reset 0x8 +10. PEF-PowerCycle 0x9 +11. SoftReset 0xA +12. RTC-Wakeup 0xB + +Change-Id: Id3b32e271b85b5fc4c69d5ca40227f8f9c08ce48 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++ + chassishandler.hpp | 1 + + host-ipmid-whitelist.conf | 1 + + 3 files changed, 56 insertions(+) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 77af2dc..2a29755 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -107,6 +107,11 @@ static constexpr auto chassisPOHStateIntf = + "xyz.openbmc_project.State.PowerOnHours"; + static constexpr auto pOHCounterProperty = "POHCounter"; + static constexpr auto match = "chassis0"; ++const static constexpr char* stateHostInterface = ++ "xyz.openbmc_project.State.Host"; ++const static constexpr char* hostRestartCauseInterface = ++ "xyz.openbmc_project.State.Host.HostRestartCause"; ++const static constexpr char* hostRestartCause = "HostRestartCause"; + const static constexpr char chassisCapIntf[] = + "xyz.openbmc_project.Control.ChassisCapabilities"; + const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags"; +@@ -324,6 +329,13 @@ struct set_sys_boot_options_t + uint8_t data[SIZE_BOOT_OPTION]; + } __attribute__((packed)); + ++struct GetSysRestartCauseResponse ++{ ++ uint8_t restartCause; ++ uint8_t channelNum; ++ ++} __attribute__((packed)); ++ + int getHostNetworkData(get_sys_boot_options_response_t* respptr) + { + ipmi::PropertyMap properties; +@@ -1598,6 +1610,44 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + return rc; + } + ++ipmi_ret_t ipmi_chassis_get_sys_restart_cause( ++ ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ++ ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) ++{ ++ ipmi_ret_t rc = IPMI_CC_OK; ++ ++ GetSysRestartCauseResponse* resp = (GetSysRestartCauseResponse*)response; ++ std::fill(reinterpret_cast<uint8_t*>(resp), ++ reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); ++ if (*data_len != 0) ++ { ++ rc = IPMI_CC_REQ_DATA_LEN_INVALID; ++ return rc; ++ } ++ ++ try ++ { ++ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; ++ ipmi::DbusObjectInfo hostObject = ++ ipmi::getDbusObject(bus, stateHostInterface); ++ ipmi::Value variant = ++ ipmi::getDbusProperty(bus, hostObject.second, hostObject.first, ++ hostRestartCauseInterface, hostRestartCause); ++ resp->restartCause = variant.get<uint8_t>(); ++ } ++ ++ catch (std::exception& e) ++ { ++ log<level::ERR>(e.what()); ++ rc = IPMI_CC_UNSPECIFIED_ERROR; ++ return rc; ++ } ++ resp->channelNum = 0; // Fix to primary channel. ++ *data_len = sizeof(GetSysRestartCauseResponse); ++ ++ return rc; ++} ++ + ipmi_ret_t ipmiGetPOHCounter(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, ipmi_response_t response, + ipmi_data_len_t data_len, ipmi_context_t context) +@@ -1739,4 +1789,8 @@ void register_netfn_chassis_functions() + ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL, + ipmi_chassis_set_power_restore_policy, + PRIVILEGE_OPERATOR); ++ ++ // <get Host Restart Cause> ++ ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_RESTART_CAUSE, NULL, ++ ipmi_chassis_get_sys_restart_cause, PRIVILEGE_USER); + } +diff --git a/chassishandler.hpp b/chassishandler.hpp +index 0c6d5a2..e37c4f1 100644 +--- a/chassishandler.hpp ++++ b/chassishandler.hpp +@@ -17,6 +17,7 @@ enum ipmi_netfn_chassis_cmds + // Set Power Restore Policy + IPMI_CMD_SET_RESTORE_POLICY = 0x06, + // Get capability bits ++ IPMI_CMD_GET_SYS_RESTART_CAUSE = 0x07, + IPMI_CMD_SET_SYS_BOOT_OPTIONS = 0x08, + IPMI_CMD_GET_SYS_BOOT_OPTIONS = 0x09, + IPMI_CMD_GET_POH_COUNTER = 0x0F, +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index db54a49..827e2dc 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -3,6 +3,7 @@ + 0x00:0x02 //<Chassis>:<Chassis Control> + 0x00:0x05 //<Chassis>:<Set Chassis Capabilities> + 0x00:0x06 //<Chassis>:<Set Power Restore Policy> ++0x00:0x07 //<Chassis>:<Get System Restart Cause> + 0x00:0x08 //<Chassis>:<Set System Boot Options> + 0x00:0x09 //<Chassis>:<Get System Boot Options> + 0x00:0x0F //<Chassis>:<Get POH Counter Command> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch new file mode 100644 index 000000000..fdaa91085 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch @@ -0,0 +1,25 @@ +From cf466ba2c66a95825ae0014d7c378ad63b050d2f Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Wed, 15 Aug 2018 14:50:04 +0800 +Subject: [PATCH] [ipmi] add oem command "get AIC FRU" to whitelist + +Intel BIOS requires this oem command to get addon card FRU info. +Add to whitelist to unblock. + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + host-ipmid-whitelist.conf | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index db54a49..49746a2 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -43,3 +43,4 @@ + 0x30:0x41 //<OEM>:<Set System GUID> + 0x30:0x26 //<OEM>:<Set BIOS ID> + 0x30:0x27 //<OEM>:<Get Device Info> ++0x30:0x31 //<OEM>:<Get AIC card FRU> +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0048-Implement-IPMI-Master-Write-Read-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0048-Implement-IPMI-Master-Write-Read-command.patch new file mode 100644 index 000000000..4018dbffe --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0048-Implement-IPMI-Master-Write-Read-command.patch @@ -0,0 +1,322 @@ +From cd25f43461b41b74d19cd1f93ce301df9c3bd4f2 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 21 Sep 2018 09:21:14 +0800 +Subject: [PATCH] Implement IPMI Master Write-Read command + +This command can be used for low-level I2C/SMBus write, read, or write-read +accesses to the IPMB or private busses behind a management controller. + +The command can also be used for providing low-level access to devices +that provide an SMBus slave interface. + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 236 ++++++++++++++++++++++++++++++++++++++++++++++ + apphandler.hpp | 1 + + host-ipmid-whitelist.conf | 1 + + 3 files changed, 238 insertions(+) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 17aff2a..2fe79f6 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -8,6 +8,14 @@ + #include "types.hpp" + #include "utils.hpp" + ++#include <fcntl.h> ++#include <linux/i2c-dev.h> ++#include <linux/i2c.h> ++#include <sys/ioctl.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <unistd.h> ++ + #include <arpa/inet.h> + #include <host-ipmid/ipmid-api.h> + #include <limits.h> +@@ -55,6 +63,8 @@ constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; + constexpr auto bmc_guid_property = "UUID"; + constexpr auto bmc_guid_len = 16; + ++static constexpr uint8_t maxIPMIWriteReadSize = 144; ++ + static constexpr auto redundancyIntf = + "xyz.openbmc_project.Software.RedundancyPriority"; + static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; +@@ -86,6 +96,34 @@ typedef struct + uint8_t aux[4]; + } __attribute__((packed)) ipmi_device_id_t; + ++typedef struct ++{ ++ uint8_t busId; ++ uint8_t slaveAddr; ++ uint8_t readCount; ++} __attribute__((packed)) ipmiI2cRwReq; ++ ++typedef struct ++{ ++ uint8_t busId; ++ uint8_t slaveAddr; ++ std::vector<uint8_t> data; ++} ipmiMasterRwWhitelist; ++ ++static std::vector<ipmiMasterRwWhitelist>& getWhiteList() ++{ ++ static std::vector<ipmiMasterRwWhitelist> rwWhiteList; ++ return rwWhiteList; ++} ++ ++static constexpr const char* whiteListFilename = ++ "/usr/share/ipmi-providers/master_write_read_white_list.json"; ++ ++static constexpr const char* filtersStr = "filters"; ++static constexpr const char* busIdStr = "busId"; ++static constexpr const char* slaveAddrStr = "slaveAddr"; ++static constexpr const char* cmdStr = "command"; ++ + /** + * @brief Returns the Version info from primary s/w object + * +@@ -1089,8 +1127,195 @@ writeResponse: + return IPMI_CC_OK; + } + ++static int loadI2CWhiteList() ++{ ++ nlohmann::json data = nullptr; ++ std::ifstream jsonFile(whiteListFilename); ++ ++ if (!jsonFile.good()) ++ { ++ log<level::WARNING>("whitelist file not found!"); ++ return -1; ++ } ++ ++ try ++ { ++ data = nlohmann::json::parse(jsonFile, nullptr, false); ++ } ++ catch (nlohmann::json::parse_error& e) ++ { ++ log<level::ERR>("Corrupted whitelist config file", ++ entry("MSG: %s", e.what())); ++ return -1; ++ } ++ ++ try ++ { ++ unsigned int i = 0; ++ nlohmann::json filters = data[filtersStr].get<nlohmann::json>(); ++ getWhiteList().resize(filters.size()); ++ ++ for (const auto& it : filters.items()) ++ { ++ nlohmann::json filter = it.value(); ++ if (filter.is_null()) ++ { ++ log<level::ERR>("Incorrect filter"); ++ return -1; ++ } ++ ++ getWhiteList()[i].busId = ++ std::stoul(filter[busIdStr].get<std::string>(), nullptr, 16); ++ ++ getWhiteList()[i].slaveAddr = std::stoul( ++ filter[slaveAddrStr].get<std::string>(), nullptr, 16); ++ ++ std::string command = filter[cmdStr].get<std::string>(); ++ ++ log<level::DEBUG>("IPMI I2C whitelist ", entry("INDEX=%d", i), ++ entry("BUS=%d", getWhiteList()[i].busId), ++ entry("ADDR=0x%x", getWhiteList()[i].slaveAddr), ++ entry("LEN=0x%x", command.length()), ++ entry("COMMAND=[%s]", command.c_str())); ++ ++ // convert data string ++ std::istringstream iss(command); ++ std::string token; ++ while (std::getline(iss, token, ' ')) ++ { ++ log<level::DEBUG>("IPMI I2C command\n", ++ entry("TOKEN=%s", token.c_str())); ++ getWhiteList()[i].data.emplace_back( ++ std::stoul(token, nullptr, 16)); ++ } ++ i++; ++ } ++ } ++ catch (std::exception& e) ++ { ++ log<level::ERR>("unexpected exception", entry("ERROR=%s", e.what())); ++ return -1; ++ } ++ return 0; ++} ++ ++ipmi_ret_t ipmiMasterWriteRead(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ++ ipmi_request_t request, ipmi_response_t response, ++ ipmi_data_len_t data_len, ipmi_context_t context) ++{ ++ bool foundInList = false; ++ int ret = 0; ++ i2c_rdwr_ioctl_data msgRdwr = {0}; ++ i2c_msg i2cmsg[2] = {0}; ++ ipmiI2cRwReq* reqi2c = reinterpret_cast<ipmiI2cRwReq*>(request); ++ ++ if (*data_len <= sizeof(ipmiI2cRwReq)) ++ { ++ log<level::ERR>("Failed in request", entry("LEN=%d", *data_len)); ++ *data_len = 0; ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ if (reqi2c->readCount > maxIPMIWriteReadSize) ++ { ++ log<level::ERR>("Failed in request", entry("R=%d", reqi2c->readCount)); ++ *data_len = 0; ++ return IPMI_CC_PARM_OUT_OF_RANGE; ++ } ++ ++ uint8_t* resptr = reinterpret_cast<uint8_t*>(response); ++ uint8_t busId = (reqi2c->busId & 0xFF) >> 1; ++ // Convert the I2C address from 7-bit format ++ uint8_t i2cAddr = reqi2c->slaveAddr >> 1; ++ size_t writeCount = *data_len - sizeof(ipmiI2cRwReq); ++ ++ log<level::DEBUG>( ++ "INPUT: ", entry("LEN=%d", *data_len), entry("ID=0x%x", busId), ++ entry("ADDR=0x%x", reqi2c->slaveAddr), entry("R=%d", reqi2c->readCount), ++ entry("W=%d", writeCount)); ++ ++ *data_len = 0; ++ ++ std::vector<uint8_t> inBuf(reqi2c->readCount); ++ std::vector<uint8_t> outBuf(writeCount); ++ uint8_t* reqptr = reinterpret_cast<uint8_t*>(request); ++ ++ reqptr += sizeof(ipmiI2cRwReq); ++ std::copy(reqptr, reqptr + writeCount, outBuf.begin()); ++ ++ log<level::DEBUG>("checking list ", entry("SIZE=%d", getWhiteList().size())); ++ // command whitelist checking ++ for (unsigned int i = 0; i < getWhiteList().size(); i++) ++ { ++ // TODO add wildchard/regex support ++ if ((busId == getWhiteList()[i].busId) && ++ (i2cAddr == getWhiteList()[i].slaveAddr) && ++ (outBuf == getWhiteList()[i].data)) ++ { ++ log<level::DEBUG>("In whitelist"); ++ foundInList = true; ++ break; ++ } ++ } ++ ++ if (!foundInList) ++ { ++ log<level::ERR>("Request blocked!", entry("BUS=%d", busId), ++ entry("ADDR=0x%x", reqi2c->slaveAddr)); ++ return IPMI_CC_INVALID_FIELD_REQUEST; ++ } ++ ++ log<level::DEBUG>("IPMI Master WriteRead ", entry("BUS=%d", busId), ++ entry("ADDR=0x%x", reqi2c->slaveAddr), ++ entry("R=%d", reqi2c->readCount), ++ entry("W=%d", writeCount)); ++ ++ std::string i2cBus = "/dev/i2c-" + std::to_string(busId); ++ ++ int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); ++ if (i2cDev < 0) ++ { ++ log<level::ERR>("Failed in opening i2c device", ++ entry("BUS=%s", i2cBus.c_str())); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ ++ // write message ++ i2cmsg[0].addr = i2cAddr; ++ i2cmsg[0].flags = 0x00; ++ i2cmsg[0].len = writeCount; ++ i2cmsg[0].buf = outBuf.data(); ++ ++ // read message ++ i2cmsg[1].addr = i2cAddr; ++ i2cmsg[1].flags = I2C_M_RD; ++ i2cmsg[1].len = reqi2c->readCount; ++ i2cmsg[1].buf = inBuf.data(); ++ ++ msgRdwr.msgs = i2cmsg; ++ msgRdwr.nmsgs = 2; ++ ++ ret = ::ioctl(i2cDev, I2C_RDWR, &msgRdwr); ++ ::close(i2cDev); ++ ++ // TODO add completion code support ++ if (ret < 0) ++ { ++ log<level::ERR>("RDWR ioctl error", entry("RET=%d", ret)); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ ++ *data_len = msgRdwr.msgs[1].len; ++ std::copy(msgRdwr.msgs[1].buf, msgRdwr.msgs[1].buf + msgRdwr.msgs[1].len, ++ resptr); ++ ++ return IPMI_CC_OK; ++} ++ + void register_netfn_app_functions() + { ++ int ret = -1; ++ + // <Get BT Interface Capabilities> + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, + ipmi_app_get_bt_capabilities, PRIVILEGE_USER); +@@ -1145,6 +1370,17 @@ void register_netfn_app_functions() + ipmi_app_channel_info, PRIVILEGE_USER); + #endif + ++ ret = loadI2CWhiteList(); ++ log<level::DEBUG>("i2c white list is loaded", entry("RET=%d", ret), ++ entry("SIZE=%d", getWhiteList().size())); ++ if (ret == 0) ++ { ++ log<level::DEBUG>("Register Master RW command"); ++ // <Master Write Read Command> ++ ipmi_register_callback(NETFUN_APP, IPMI_CMD_MASTER_WRITE_READ, NULL, ++ ipmiMasterWriteRead, PRIVILEGE_OPERATOR); ++ } ++ + // <Get System GUID Command> + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, + ipmi_app_get_sys_guid, PRIVILEGE_USER); +diff --git a/apphandler.hpp b/apphandler.hpp +index d4dd8e8..f9e5c59 100644 +--- a/apphandler.hpp ++++ b/apphandler.hpp +@@ -19,6 +19,7 @@ enum ipmi_netfn_app_cmds + IPMI_CMD_SET_CHAN_ACCESS = 0x40, + IPMI_CMD_GET_CHANNEL_ACCESS = 0x41, + IPMI_CMD_GET_CHAN_INFO = 0x42, ++ IPMI_CMD_MASTER_WRITE_READ = 0x52, + IPMI_CMD_GET_CHAN_CIPHER_SUITES = 0x54, + IPMI_CMD_SET_SYSTEM_INFO = 0x58, + IPMI_CMD_GET_SYSTEM_INFO = 0x59, +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index c7eb2d8..22a2a3c 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -25,6 +25,7 @@ + 0x06:0x36 //<App>:<Get BT Interface Capabilities> + 0x06:0x37 //<App>:<Get System GUID> + 0x06:0x42 //<App>:<Get Channel Info Command> ++0x06:0x52 //<App>:<Master Write Read Command> + 0x06:0x54 //<App>:<Get Channel Cipher Suites> + 0x0A:0x10 //<Storage>:<Get FRU Inventory Area Info> + 0x0A:0x11 //<Storage>:<Read FRU Data> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch new file mode 100644 index 000000000..aba5eb095 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch @@ -0,0 +1,71 @@ +From 59287a8869b5253a1b4203e0cc8a92f063dcc7e6 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Tue, 25 Sep 2018 16:08:22 +0800 +Subject: [PATCH] Fix "Unspecified error" on ipmi restart cause command + +Needs to convert the dbus value(enum) into ipmi value(uint8) + +Tested by: +ipmitool chassis restart_cause + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 28 ++++++++++++++++++++++++++-- + 1 file changed, 26 insertions(+), 2 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 40eb4f5..c3d4931 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -106,7 +106,7 @@ static constexpr auto match = "chassis0"; + const static constexpr char* stateHostInterface = + "xyz.openbmc_project.State.Host"; + const static constexpr char* hostRestartCauseInterface = +- "xyz.openbmc_project.State.Host.HostRestartCause"; ++ "xyz.openbmc_project.State.Host"; + const static constexpr char* hostRestartCause = "HostRestartCause"; + const static constexpr char chassisCapIntf[] = + "xyz.openbmc_project.Control.ChassisCapabilities"; +@@ -1764,6 +1764,26 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + return rc; + } + ++namespace restart_cause ++{ ++ ++using namespace sdbusplus::xyz::openbmc_project::State::server; ++ ++std::map<Host::RestartCause, uint8_t> dbusToIpmi = { ++ {Host::RestartCause::Unknown, 0x0}, ++ {Host::RestartCause::IpmiCommand, 0x1}, ++ {Host::RestartCause::ResetButton, 0x2}, ++ {Host::RestartCause::PowerButton, 0x3}, ++ {Host::RestartCause::WatchdogTimer, 0x4}, ++ {Host::RestartCause::OEM, 0x5}, ++ {Host::RestartCause::PowerPolicyAlwaysOn, 0x6}, ++ {Host::RestartCause::PowerPolicyPreviousState, 0x7}, ++ {Host::RestartCause::PEFReset, 0x8}, ++ {Host::RestartCause::PEFPowerCycle, 0x9}, ++ {Host::RestartCause::SoftReset, 0xa}, ++ {Host::RestartCause::RTCWakeup, 0xb}}; ++} // namespace restart_cause ++ + ipmi_ret_t ipmi_chassis_get_sys_restart_cause( + ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, + ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) +@@ -1787,7 +1807,11 @@ ipmi_ret_t ipmi_chassis_get_sys_restart_cause( + ipmi::Value variant = + ipmi::getDbusProperty(bus, hostObject.second, hostObject.first, + hostRestartCauseInterface, hostRestartCause); +- resp->restartCause = variant.get<uint8_t>(); ++ ++ std::string restartCause = ++ sdbusplus::message::variant_ns::get<std::string>(variant); ++ resp->restartCause = restart_cause::dbusToIpmi.at( ++ restart_cause::Host::convertRestartCauseFromString(restartCause)); + } + + catch (std::exception& e) +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0050-enable-6-oem-commands.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0050-enable-6-oem-commands.patch new file mode 100644 index 000000000..b800632cc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0050-enable-6-oem-commands.patch @@ -0,0 +1,15 @@ +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 22a2a3c..5d71698 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -49,3 +49,10 @@ + 0x30:0x26 //<OEM>:<Set BIOS ID> + 0x30:0x27 //<OEM>:<Get Device Info> + 0x30:0x31 //<OEM>:<Get AIC card FRU> ++0x30:0x54 //<OEM>:<Set Power Restore Delay> ++0x30:0x55 //<OEM>:<Get Power Restore Delay> ++0x30:0x9A //<OEM>:<Get Processor Error Config> ++0x30:0x9B //<OEM>:<Set Processor Error Config> ++0x30:0xB0 //<OEM>:<Get LED Status> ++0x30:0xE9 //<OEM>:<Get BIOS Post Codes> ++ diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch new file mode 100644 index 000000000..3990c6b5c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch @@ -0,0 +1,142 @@ +From cae9e21f88e6f12c80c89402473a17a10258c843 Mon Sep 17 00:00:00 2001 +From: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +Date: Thu, 17 Jan 2019 21:22:30 +0530 +Subject: [PATCH] Fix: Set LAN Config to work without SetInProgress + +Set LAN Configuration parameters in up-stream code works +with SetInProgress (parameter selector 0), to be marked +as SET_IN_PROGRESS before fields update, and SET_COMPLETE to +make the changes effective. This is not mandatory as per +IPMI Spec, and we must support individual fields update. +Fix: +1. After SET_COMPLETE for parameter selector, changes has +to be applied immediately, and doesn't require to rely on +network timer, as purpose of this logic itself is to stage +and commit. +2. Allow individual parameter changes to take effect based +on timer. For the time being reduced the timer to 5 sec +to have quicker turn-around and group things together. + +TODO: +Still need to introduce lock between ChannelConfig variable +between Timer & Get / Set LAN Configuration command to avoid +race condition + +Unit-Test: +1. Verified the BIOS Setup page, able to set the IPV4 to static +IP, afte disabling IPV6, and configuring IPV4 to static, after +save and reset, the changes of IPV4 static is preserved. + +Change-Id: I7c2edad2861b5dba5ad1ca97cc5e39ac02871746 +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +--- + transporthandler.cpp | 54 ++++++++++++++++++++++++++++++++++++---------------- + transporthandler.hpp | 2 ++ + 2 files changed, 40 insertions(+), 16 deletions(-) + +Index: phosphor-host-ipmid.clean/transporthandler.cpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.cpp ++++ phosphor-host-ipmid.clean/transporthandler.cpp +@@ -399,6 +399,41 @@ struct set_lan_t + uint8_t data[8]; // Per IPMI spec, not expecting more than this size + } __attribute__((packed)); + ++ipmi_ret_t checkAndUpdateNetwork(int channel) ++{ ++ auto channelConf = getChannelConfig(channel); ++ using namespace std::chrono_literals; ++ // time to wait before applying the network changes. ++ constexpr auto networkTimeout = 5000000us; // 5 sec ++ ++ if (channelConf->lan_set_in_progress == SET_COMPLETE && ++ ((channelConf->flush == false) || ++ (channelConf->updateInProgress == true))) ++ { ++ channelConf->flush = true; ++ // used to indicate that network timer update is in progress. ++ channelConf->updateInProgress = true; ++ if (!networkTimer) ++ { ++ log<level::ERR>("Network timer is not instantiated"); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ // start/restart the timer ++ // TODO: Need to implement locking mechansim between networkTimer & ++ // get/set to avoid race condition. ++ networkTimer->start(networkTimeout); ++ } ++ else if (channelConf->lan_set_in_progress == SET_COMPLETE && ++ channelConf->flush == true && ++ channelConf->updateInProgress == false) ++ { ++ // Apply the network changes immediately, if proper SET_IN_PROGRESS, ++ // followed by SET_COMPLETE is issued. ++ applyChanges(channel); ++ } ++ return IPMI_CC_OK; ++} ++ + ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, +@@ -406,12 +441,6 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + ipmi_context_t context) + { + ipmi_ret_t rc = IPMI_CC_OK; +- +- using namespace std::chrono_literals; +- +- // time to wait before applying the network changes. +- constexpr auto networkTimeout = 10000000us; // 10 sec +- + char ipaddr[INET_ADDRSTRLEN]; + char netmask[INET_ADDRSTRLEN]; + char gateway[INET_ADDRSTRLEN]; +@@ -543,15 +572,6 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + entry("ADDRESS=%s", channelConf->ipaddr.c_str()), + entry("GATEWAY=%s", channelConf->gateway.c_str()), + entry("VLAN=%d", channelConf->vlanID)); +- +- if (!networkTimer) +- { +- log<level::ERR>("Network timer is not instantiated"); +- return IPMI_CC_UNSPECIFIED_ERROR; +- } +- +- // start/restart the timer +- networkTimer->start(networkTimeout); + } + else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress + { +@@ -680,8 +700,10 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + default: + { + rc = IPMI_CC_PARM_NOT_SUPPORTED; ++ return rc; + } + } ++ rc = checkAndUpdateNetwork(channel); + + return rc; + } +Index: phosphor-host-ipmid.clean/transporthandler.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.hpp ++++ phosphor-host-ipmid.clean/transporthandler.hpp +@@ -140,6 +140,7 @@ struct ChannelConfig_t + // vlan id is in 12 bits and the 16th bit is for enable mask. + uint32_t vlanID = ipmi::network::VLAN_ID_MASK; + uint8_t lan_set_in_progress = SET_COMPLETE; ++ uint8_t updateInProgress = false; + bool flush = false; + + // IPV6 parameters +@@ -165,6 +166,7 @@ struct ChannelConfig_t + vlanID = ipmi::network::VLAN_ID_MASK; + ipsrc = ipmi::network::IPOrigin::UNSPECIFIED; + lan_set_in_progress = SET_COMPLETE; ++ updateInProgress = false; + flush = false; + + // IPv6 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch new file mode 100644 index 000000000..2a4cc9bb1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch @@ -0,0 +1,80 @@ +From 9ed3fd11047f8c360b7d808946939ef280813811 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@linux.intel.com> +Date: Wed, 23 Jan 2019 17:02:40 +0800 +Subject: [PATCH] Fix keep looping issue when entering OS + +Sometimes when entering OS, OS will keep continuously sending ipmi command +"READ EVENT MESSAGE BUFFER" to BMC. This issue is caused by incorrect KCS +status. If restart the host immediately while OS is still running, SMS_ATN +will be set, after that KCS come into an incorrect status, and then KCS +communction between BMC and OS crash. To make KCS go back to correct status +and fix the issue, clear SMS_ATN after every time power cycle happen. + +Unit Test: + After entered OS, force reset system, after enter OS again, OS can start +normally without keep sending READ EVENT MESSAGE BUFFER command. + After power on system, enter EFI SHELL, check cmdtool.efi can work +correctly through KCS channel. +--- + host-cmd-manager.cpp | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp +index 0a61e63..6e50684 100644 +--- a/host-cmd-manager.cpp ++++ b/host-cmd-manager.cpp +@@ -26,6 +26,8 @@ constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; + constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; + constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host"; + constexpr auto HOST_TRANS_PROP = "RequestedHostTransition"; ++constexpr const char* IPMI_PATH = "/org/openbmc/HostIpmi/1"; ++constexpr const char* IPMI_INTERFACE = "org.openbmc.HostIpmi"; + + // For throwing exceptions + using namespace phosphor::logging; +@@ -107,6 +109,20 @@ void Manager::clearQueue() + // `false` indicating Failure + std::get<CallBack>(command)(ipmiCmdData, false); + } ++ ++ auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH); ++ auto method = this->bus.new_method_call(host.c_str(), IPMI_PATH, ++ IPMI_INTERFACE, "clearAttention"); ++ ++ try ++ { ++ auto reply = this->bus.call(method); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ log<level::ERR>("Error in clearing SMS attention"); ++ elog<InternalFailure>(); ++ } + } + + // Called for alerting the host +@@ -116,9 +132,6 @@ void Manager::checkQueueAndAlertHost() + { + log<level::DEBUG>("Asserting SMS Attention"); + +- std::string IPMI_PATH("/org/openbmc/HostIpmi/1"); +- std::string IPMI_INTERFACE("org.openbmc.HostIpmi"); +- + auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH); + + // Start the timer for this transaction +@@ -132,9 +145,8 @@ void Manager::checkQueueAndAlertHost() + return; + } + +- auto method = +- this->bus.new_method_call(host.c_str(), IPMI_PATH.c_str(), +- IPMI_INTERFACE.c_str(), "setAttention"); ++ auto method = this->bus.new_method_call(host.c_str(), IPMI_PATH, ++ IPMI_INTERFACE, "setAttention"); + auto reply = this->bus.call(method); + + if (reply.is_method_error()) +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0054-Fix-User-commands-require-channel-layer-lib.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0054-Fix-User-commands-require-channel-layer-lib.patch new file mode 100644 index 000000000..bf6f672cf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0054-Fix-User-commands-require-channel-layer-lib.patch @@ -0,0 +1,37 @@ +From 5d0c9d2217dbe369daffb8a92d7b5e7d7d34d566 Mon Sep 17 00:00:00 2001 +From: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +Date: Sat, 2 Mar 2019 20:08:32 +0530 +Subject: [PATCH] Fix: User commands require channel layer lib + +As channel layer is separated out from user layer lib, it +has to be manually included in libusercommands, as user +command handlers use channel layer API's + +Tested-by: +1. Made sure that libusercommands are loaded on it's own +without any undefined symbol error. +2. ipmitool user list 1 works on host interface + +Change-Id: I6652ad248e01afc1349e3a9612754dbdb84b96ad +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +--- + Makefile.am | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index bb7bdbf..4e9101e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -120,7 +120,8 @@ libipmi20_la_CXXFLAGS = $(COMMON_CXX) + providers_LTLIBRARIES += libusercmds.la + libusercmds_la_LIBADD = \ + libipmid/libipmid.la \ +- user_channel/libuserlayer.la ++ user_channel/libuserlayer.la \ ++ user_channel/libchannellayer.la + libusercmds_la_SOURCES = \ + user_channel/usercommands.cpp + libusercmds_la_LDFLAGS = \ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0055-Implement-set-front-panel-button-enables-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0055-Implement-set-front-panel-button-enables-command.patch new file mode 100644 index 000000000..170e530f9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0055-Implement-set-front-panel-button-enables-command.patch @@ -0,0 +1,185 @@ +From b8b88a5c0f9e9cb6023cb8d5453e5cfadaa1a375 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Fri, 4 Jan 2019 10:50:21 +0800 +Subject: [PATCH] Implement set front panel button enables command + +Through modify buttons' property "Enabled" to disable/enable +corresponding button. +Currently support power and reset button. + +Test-By: ipmitool raw 0x0 0xa 0x2 //disable reset button + ipmitool raw 0x0 0xa 0x1 //disable power button + ipmitool raw 0x0 0xa 0x0 //enable all buttons + +Change-Id: Ice6f58edb898689f7a7fa08ad078d25fccaab27e +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + chassishandler.cpp | 98 +++++++++++++++++++++++++++++++++++++++ + chassishandler.hpp | 1 + + host-ipmid-whitelist.conf | 1 + + 3 files changed, 100 insertions(+) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 4b42b3c..1a5b805 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -112,6 +112,8 @@ const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress"; + const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress"; + static constexpr uint8_t chassisCapFlagMask = 0x0f; + static constexpr uint8_t chassisCapAddrMask = 0xfe; ++static constexpr uint8_t disableResetButton = 0x2; ++static constexpr uint8_t disablePowerButton = 0x1; + + typedef struct + { +@@ -140,6 +142,19 @@ struct GetPOHCountResponse + uint8_t counterReading[4]; ///< Counter reading + } __attribute__((packed)); + ++typedef struct ++{ ++ uint8_t disables; // Front Panel Button Enables ++ //[7:4] - reserved ++ //[3] - 1b = disable Standby (sleep) button for entering standby (sleep) ++ //(control can still be used to wake the system) ++ //[2] - 1b = disable Diagnostic Interrupt button ++ //[1] - 1b = disable Reset button ++ //[0] - 1b = disable Power off button for power off only (in the case there ++ // is a single combined power/standby (sleep) button, then this also ++ // disables sleep requests via that button) ++} __attribute__((packed)) IPMISetFrontPanelButtonEnablesReq; ++ + // Phosphor Host State manager + namespace State = sdbusplus::xyz::openbmc_project::State::server; + +@@ -948,6 +963,8 @@ ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + + // Front Panel Button Capabilities and disable/enable status(Optional) + // set to 0, for we don't support them. ++ // TODO, it is tracked by an issue: ++ // https://github.com/openbmc/phosphor-host-ipmid/issues/122 + chassis_status.front_panel_button_cap_status = 0; + + // Pack the actual response +@@ -1721,6 +1738,82 @@ ipmi_ret_t ipmi_chassis_set_power_restore_policy( + return IPMI_CC_OK; + } + ++ipmi_ret_t ipmiSetFrontPanelButtonEnables(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ++ ipmi_request_t request, ++ ipmi_response_t response, ++ ipmi_data_len_t data_len, ++ ipmi_context_t context) ++{ ++ bool enable = false; ++ constexpr const char* powerButtonIntf = ++ "xyz.openbmc_project.Chassis.Buttons.Power"; ++ constexpr const char* powerButtonPath = ++ "/xyz/openbmc_project/Chassis/Buttons/Power0"; ++ constexpr const char* resetButtonIntf = ++ "xyz.openbmc_project.Chassis.Buttons.Reset"; ++ constexpr const char* resetButtonPath = ++ "/xyz/openbmc_project/Chassis/Buttons/Reset0"; ++ using namespace chassis::internal; ++ ++ IPMISetFrontPanelButtonEnablesReq* req = ++ static_cast<IPMISetFrontPanelButtonEnablesReq*>(request); ++ if (*data_len != 1) ++ { ++ *data_len = 0; ++ log<level::ERR>("IPMI request len is invalid"); ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ *data_len = 0; ++ if (req->disables & disablePowerButton) ++ { ++ // Disable power button ++ enable = false; ++ } ++ else ++ { ++ // Enable power button ++ enable = true; ++ } ++ // set power button Enabled property ++ try ++ { ++ auto service = ipmi::getService(dbus, powerButtonIntf, powerButtonPath); ++ ipmi::setDbusProperty(dbus, service, powerButtonPath, powerButtonIntf, ++ "Enabled", enable); ++ } ++ catch (sdbusplus::exception::SdBusError& e) ++ { ++ log<level::ERR>(e.what()); ++ log<level::ERR>("Fail to set power button Enabled property"); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ ++ if (req->disables & disableResetButton) ++ { ++ // disable reset button ++ enable = false; ++ } ++ else ++ { ++ // enable reset button ++ enable = true; ++ } ++ // set reset button Enabled property ++ try ++ { ++ auto service = ipmi::getService(dbus, resetButtonIntf, resetButtonPath); ++ ipmi::setDbusProperty(dbus, service, resetButtonPath, resetButtonIntf, ++ "Enabled", enable); ++ } ++ catch (sdbusplus::exception::SdBusError& e) ++ { ++ log<level::ERR>(e.what()); ++ log<level::ERR>("Fail to set reset button Enabled property"); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ return IPMI_CC_OK; ++} ++ + void register_netfn_chassis_functions() + { + createIdentifyTimer(); +@@ -1733,6 +1826,11 @@ void register_netfn_chassis_functions() + ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL, + ipmi_get_chassis_cap, PRIVILEGE_USER); + ++ // Set Front Panel Button Enables ++ ipmi_register_callback(NETFUN_CHASSIS, ++ IPMI_CMD_SET_FRONT_PANEL_BUTTON_ENABLES, NULL, ++ ipmiSetFrontPanelButtonEnables, PRIVILEGE_ADMIN); ++ + // Set Chassis Capabilities + ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL, + ipmi_set_chassis_cap, PRIVILEGE_USER); +diff --git a/chassishandler.hpp b/chassishandler.hpp +index 49b5ef8..f4a6bff 100644 +--- a/chassishandler.hpp ++++ b/chassishandler.hpp +@@ -19,6 +19,7 @@ enum ipmi_netfn_chassis_cmds + // Get capability bits + IPMI_CMD_SET_SYS_BOOT_OPTIONS = 0x08, + IPMI_CMD_GET_SYS_BOOT_OPTIONS = 0x09, ++ IPMI_CMD_SET_FRONT_PANEL_BUTTON_ENABLES = 0x0A, + IPMI_CMD_GET_POH_COUNTER = 0x0F, + }; + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index e5cd0b5..d96d9ed 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -6,6 +6,7 @@ + 0x00:0x06 //<Chassis>:<Set Power Restore Policy> + 0x00:0x08 //<Chassis>:<Set System Boot Options> + 0x00:0x09 //<Chassis>:<Get System Boot Options> ++0x00:0x0A //<Chassis>:<Set Front Panel Button Enables> + 0x00:0x0F //<Chassis>:<Get POH Counter Command> + 0x04:0x2D //<Sensor/Event>:<Get Sensor Reading> + 0x04:0x2F //<Sensor/Event>:<Get Sensor Type> +-- +2.19.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0056-add-SetInProgress-to-get-set-boot-option-cmd.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0056-add-SetInProgress-to-get-set-boot-option-cmd.patch new file mode 100644 index 000000000..3a77887a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0056-add-SetInProgress-to-get-set-boot-option-cmd.patch @@ -0,0 +1,102 @@ +From 949db3a985719335d3df77db368eb2b296756749 Mon Sep 17 00:00:00 2001 +From: "Jia, chunhui" <chunhui.jia@linux.intel.com> +Date: Tue, 19 Mar 2019 16:09:06 +0800 +Subject: [PATCH] add SetInProgress to get/set boot option cmd + +It is required by BIOS. BIOS will check setinprogress first. +If this flag is not supported, BIOS will bypass all boot +option flow. + +Change-Id: Ibb0501ea5bc36c4f1f72339efef03724dd4e613f +Signed-off-by: Jia, chunhui <chunhui.jia@linux.intel.com> +--- + chassishandler.cpp | 26 +++++++++++++++++++++++++- + chassishandler.hpp | 3 +++ + 2 files changed, 28 insertions(+), 1 deletion(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 6d14d1b..553afa8 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -1351,6 +1351,10 @@ static ipmi_ret_t setBootMode(const Mode::Modes& mode) + return IPMI_CC_OK; + } + ++static constexpr uint8_t setComplete = 0x0; ++static constexpr uint8_t setInProgress = 0x1; ++static uint8_t transferStatus = setComplete; ++ + ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, +@@ -1365,11 +1369,21 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request; + IpmiValue bootOption = ipmiDefault; + ++ if (reqptr->parameter == ++ static_cast<uint8_t>(BootOptionParameter::SET_IN_PROGRESS)) ++ { ++ *data_len = ++ static_cast<uint8_t>(BootOptionResponseSize::SET_IN_PROGRESS); ++ resp->version = SET_PARM_VERSION; ++ resp->parm = static_cast<uint8_t>(BootOptionParameter::SET_IN_PROGRESS); ++ resp->data[0] = transferStatus; ++ return IPMI_CC_OK; ++ } ++ + std::memset(resp, 0, sizeof(*resp)); + resp->version = SET_PARM_VERSION; + resp->parm = 5; + resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME; +- + /* + * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. + * This is the only parameter used by petitboot. +@@ -1505,6 +1519,16 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + // This IPMI command does not have any resposne data + *data_len = 0; + ++ if (reqptr->parameter == ++ static_cast<uint8_t>(BootOptionParameter::SET_IN_PROGRESS)) ++ { ++ if (transferStatus == setInProgress) { ++ return IPMI_CC_FAIL_SET_IN_PROGRESS; ++ } ++ transferStatus = reqptr->data[0]; ++ return IPMI_CC_OK; ++ } ++ + /* 000101 + * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. + * This is the only parameter used by petitboot. +diff --git a/chassishandler.hpp b/chassishandler.hpp +index 2c42b11..6a24507 100644 +--- a/chassishandler.hpp ++++ b/chassishandler.hpp +@@ -28,6 +28,7 @@ enum ipmi_chassis_return_codes + { + IPMI_OK = 0x0, + IPMI_CC_PARM_NOT_SUPPORTED = 0x80, ++ IPMI_CC_FAIL_SET_IN_PROGRESS = 0x81, + }; + + // Generic completion codes, +@@ -49,6 +50,7 @@ enum ipmi_chassis_control_cmds : uint8_t + }; + enum class BootOptionParameter : size_t + { ++ SET_IN_PROGRESS = 0x0, + BOOT_INFO = 0x4, + BOOT_FLAGS = 0x5, + OPAL_NETWORK_SETTINGS = 0x61 +@@ -56,6 +58,7 @@ enum class BootOptionParameter : size_t + + enum class BootOptionResponseSize : size_t + { ++ SET_IN_PROGRESS = 3, + BOOT_FLAGS = 5, + OPAL_NETWORK_SETTINGS = 50 + }; +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0057-Add-timer-use-actions-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0057-Add-timer-use-actions-support.patch new file mode 100644 index 000000000..5813cceae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0057-Add-timer-use-actions-support.patch @@ -0,0 +1,195 @@ +From 6e37e02a4f200507627a82f6dba00a9c9d877cb2 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 18 Mar 2019 23:05:16 +0800 +Subject: [PATCH] Add timer use/actions support + +Based on IPMI spec, add timer use/actions support, +and add input data checking + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + app/watchdog.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++----- + app/watchdog_service.cpp | 8 +++++++ + app/watchdog_service.hpp | 8 +++++++ + 3 files changed, 72 insertions(+), 6 deletions(-) + +diff --git a/app/watchdog.cpp b/app/watchdog.cpp +index 1a5d19c..3b61055 100644 +--- a/app/watchdog.cpp ++++ b/app/watchdog.cpp +@@ -89,6 +89,13 @@ static constexpr uint8_t wd_dont_stop = 0x1 << 6; + static constexpr uint8_t wd_timeout_action_mask = 0x3; + + static constexpr uint8_t wdTimerUseMask = 0x7; ++static constexpr uint8_t wdTimerUseResTimer1 = 0x0; ++static constexpr uint8_t wdTimerUseResTimer2 = 0x6; ++static constexpr uint8_t wdTimerUseResTimer3 = 0x7; ++static constexpr uint8_t wdTimerUseRes = 0x38; ++ ++static constexpr uint8_t wdTimerActionMask = 0xcc; ++static constexpr uint8_t wdTimerUseExpMask = 0xc1; + + enum class IpmiAction : uint8_t + { +@@ -186,6 +193,11 @@ static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size."); + static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER, + "wd_get_res can't fit in request buffer."); + ++static uint8_t timerLogFlags = 0; ++static uint8_t timerActions = 0; ++ ++static uint8_t timerUseExpirationFlags = 0; ++ + ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, +@@ -203,6 +215,24 @@ ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + req.initial_countdown = le16toh(req.initial_countdown); + *data_len = 0; + ++ if (((req.timer_use & wdTimerUseMask) == wdTimerUseResTimer1) || ++ ((req.timer_use & wdTimerUseMask) == wdTimerUseResTimer2) || ++ ((req.timer_use & wdTimerUseMask) == wdTimerUseResTimer3) || ++ (req.timer_use & wdTimerUseRes) || ++ (req.timer_action & wdTimerActionMask) || ++ (req.expire_flags & wdTimerUseExpMask)) ++ { ++ return IPMI_CC_INVALID_FIELD_REQUEST; ++ } ++ ++ if (req.pretimeout > (req.initial_countdown / 10)) ++ { ++ return IPMI_CC_INVALID_FIELD_REQUEST; ++ } ++ ++ timerLogFlags = req.timer_use & 0x80; ++ timerActions = req.timer_action; ++ + try + { + WatchdogService wd_service; +@@ -221,6 +251,10 @@ ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + static_cast<IpmiTimerUse>(req.timer_use & wdTimerUseMask); + wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse)); + ++ wd_service.setExpiredTimerUse(WatchdogService::TimerUse::Reserved); ++ ++ timerUseExpirationFlags &= ~req.expire_flags; ++ + // Set the new interval and the time remaining deci -> mill seconds + const uint64_t interval = req.initial_countdown * 100; + wd_service.setInterval(interval); +@@ -339,7 +373,6 @@ static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size."); + static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER, + "wd_get_res can't fit in response buffer."); + +-static constexpr uint8_t wd_dont_log = 0x1 << 7; + static constexpr uint8_t wd_running = 0x1 << 6; + + ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd, +@@ -358,20 +391,37 @@ ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + + // Build and return the response + wd_get_res res; +- res.timer_use = wd_dont_log; +- res.timer_action = +- static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction)); ++ res.timer_use |= timerLogFlags; ++ res.timer_action = timerActions; + + // Interval and timeRemaining need converted from milli -> deci seconds + res.initial_countdown = htole16(wd_prop.interval / 100); ++ ++ if (wd_prop.expiredTimerUse != WatchdogService::TimerUse::Reserved) ++ { ++ timerUseExpirationFlags |= ++ 1 << static_cast<uint8_t>( ++ wdTimerUseToIpmiTimerUse(wd_prop.expiredTimerUse)); ++ } ++ + if (wd_prop.enabled) + { + res.timer_use |= wd_running; + res.present_countdown = htole16(wd_prop.timeRemaining / 100); ++ res.expire_flags = 0; + } + else + { +- res.present_countdown = res.initial_countdown; ++ if (wd_prop.expiredTimerUse == WatchdogService::TimerUse::Reserved) ++ { ++ res.present_countdown = res.initial_countdown; ++ res.expire_flags = 0; ++ } ++ else ++ { ++ res.present_countdown = 0; ++ res.expire_flags = timerUseExpirationFlags; ++ } + } + + res.timer_use |= +@@ -379,7 +429,7 @@ ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + + // TODO: Do something about having pretimeout support + res.pretimeout = 0; +- res.expire_flags = 0; ++ + memcpy(response, &res, sizeof(res)); + *data_len = sizeof(res); + lastCallSuccessful = true; +diff --git a/app/watchdog_service.cpp b/app/watchdog_service.cpp +index e65ea63..8b1aa47 100644 +--- a/app/watchdog_service.cpp ++++ b/app/watchdog_service.cpp +@@ -83,6 +83,9 @@ WatchdogService::Properties WatchdogService::getProperties() + wd_prop.timerUse = Watchdog::convertTimerUseFromString( + get<std::string>(properties.at("CurrentTimerUse"))); + ++ wd_prop.expiredTimerUse = Watchdog::convertTimerUseFromString( ++ get<std::string>(properties.at("ExpiredTimerUse"))); ++ + wd_prop.interval = get<uint64_t>(properties.at("Interval")); + wd_prop.timeRemaining = get<uint64_t>(properties.at("TimeRemaining")); + return wd_prop; +@@ -187,6 +190,11 @@ void WatchdogService::setTimerUse(TimerUse timerUse) + setProperty("CurrentTimerUse", convertForMessage(timerUse)); + } + ++void WatchdogService::setExpiredTimerUse(TimerUse timerUse) ++{ ++ setProperty("ExpiredTimerUse", convertForMessage(timerUse)); ++} ++ + void WatchdogService::setInterval(uint64_t interval) + { + setProperty("Interval", interval); +diff --git a/app/watchdog_service.hpp b/app/watchdog_service.hpp +index 75afc1e..d0cc1a8 100644 +--- a/app/watchdog_service.hpp ++++ b/app/watchdog_service.hpp +@@ -36,6 +36,7 @@ class WatchdogService + bool enabled; + Action expireAction; + TimerUse timerUse; ++ TimerUse expiredTimerUse; + uint64_t interval; + uint64_t timeRemaining; + }; +@@ -79,6 +80,13 @@ class WatchdogService + */ + void setTimerUse(TimerUse timerUse); + ++ /** @brief Sets the value of the ExpiredTimerUse property on the host ++ * watchdog ++ * ++ * @param[in] timerUse - The new timerUse value ++ */ ++ void setExpiredTimerUse(TimerUse timerUse); ++ + /** @brief Sets the value of the interval property on the host watchdog + * + * @param[in] interval - The new interval value +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service new file mode 100644 index 000000000..d855eaa5b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service @@ -0,0 +1,26 @@ +[Unit] +Description=Phosphor Inband IPMI +# TODO openbmc/openbmc#2059 - The wants/after below should be based on providers +Wants=mapper-wait@-xyz-openbmc_project-control-host0-boot.service +After=mapper-wait@-xyz-openbmc_project-control-host0-boot.service +Wants=mapper-wait@-xyz-openbmc_project-control-host0-boot-one_time.service +After=mapper-wait@-xyz-openbmc_project-control-host0-boot-one_time.service +Wants=mapper-wait@-xyz-openbmc_project-control-host0-power_restore_policy.service +After=mapper-wait@-xyz-openbmc_project-control-host0-power_restore_policy.service +Wants=mapper-wait@-xyz-openbmc_project-control-host0-restriction_mode.service +After=mapper-wait@-xyz-openbmc_project-control-host0-restriction_mode.service +Wants=clear-once.service +After=clear-once.service + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStart=/usr/bin/env ipmid +SyslogIdentifier=ipmid +RuntimeDirectory = ipmi +RuntimeDirectoryPreserve = yes +StateDirectory = ipmi + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend new file mode 100644 index 000000000..2d47fdfff --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend @@ -0,0 +1,37 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +# TODO: This should be removed, once up-stream bump up +# issue is resolved +#SRC_URI = "git://github.com/openbmc/phosphor-host-ipmid" +SRCREV = "55768e3548ef7476d4fdbe7be7a3ddb4d4896f14" + +SRC_URI += "file://phosphor-ipmi-host.service \ + file://0002-Modify-dbus-interface-for-power-control.patch \ + file://0003-Modify-dbus-interface-for-chassis-control.patch \ + file://0009-IPv6-Network-changes.patch \ + file://0010-fix-get-system-GUID-ipmi-command.patch \ + file://0012-ipmi-set-get-boot-options.patch \ + file://0013-ipmi-add-set-bios-id-to-whitelist.patch \ + file://0014-Enable-get-device-guid-ipmi-command.patch \ + file://0016-add-better-sdbusplus-exception-handling.patch \ + file://0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch \ + file://0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch \ + file://0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch \ + file://0048-Implement-IPMI-Master-Write-Read-command.patch \ + file://0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch \ + file://0050-enable-6-oem-commands.patch \ + file://0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch \ + file://0053-Fix-keep-looping-issue-when-entering-OS.patch \ + file://0055-Implement-set-front-panel-button-enables-command.patch \ + file://0056-add-SetInProgress-to-get-set-boot-option-cmd.patch \ + file://0057-Add-timer-use-actions-support.patch \ + " + +do_install_append(){ + install -d ${D}${includedir}/phosphor-ipmi-host + install -d ${D}${libdir}/phosphor-ipmi-host + install -m 0644 -D ${S}/*.h ${D}${includedir}/phosphor-ipmi-host + install -m 0644 -D ${S}/*.hpp ${D}${includedir}/phosphor-ipmi-host + install -m 0644 -D ${S}/utils.cpp ${D}${libdir}/phosphor-ipmi-host + +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend new file mode 100644 index 000000000..d5d38a0ce --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend @@ -0,0 +1,2 @@ +SRC_URI = "git://github.com/openbmc/ipmbbridge.git" +SRCREV = "25e85c79257723b1cb754c20299196685373ce24" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.SMM.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.SMM.service new file mode 100644 index 000000000..288fa422d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.SMM.service @@ -0,0 +1,13 @@ +[Unit] +Description=Phosphor IPMI KCS DBus Bridge(SMM) +After=phosphor-ipmi-host.service + +[Service] +Restart=always +ExecStart={sbindir}/kcsbridged --d="/dev/ipmi-kcs4" --i="SMM" +SyslogIdentifier=kcsbridged_SMM +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.service new file mode 100644 index 000000000..177062e27 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.service @@ -0,0 +1,13 @@ +[Unit] +Description=Phosphor IPMI KCS DBus Bridge(SMS) +After=phosphor-ipmi-host.service + +[Service] +Restart=always +ExecStart={sbindir}/kcsbridged --d="/dev/ipmi-kcs3" +SyslogIdentifier=kcsbridged +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend new file mode 100644 index 000000000..ac7a03108 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +DBUS_SERVICE_${PN} += "org.openbmc.HostIpmi.SMM.service" + +SYSTEMD_SUBSTITUTIONS_remove = "KCS_DEVICE:${KCS_DEVICE}:${DBUS_SERVICE_${PN}}" + +SRC_URI = "git://github.com/openbmc/kcsbridge.git" +SRCREV = "17a2ab7f39a78ff0603aa68cf35108ea94eb442f" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch new file mode 100644 index 000000000..7225c7529 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch @@ -0,0 +1,39 @@ +From 6fc55bb689272d34ff6616cdd4b24367ea39c749 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Mon, 2 Jul 2018 15:51:52 +0800 +Subject: [PATCH] Modify dbus namespace of chassis control for guid.cpp +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Switch chassis control service namespace for guid.cpp from “org” to “xyz”, +to compatible with new intel-chassis services + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + command/guid.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +Index: phosphor-net-ipmid.clean/command/guid.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/guid.cpp ++++ phosphor-net-ipmid.clean/command/guid.cpp +@@ -21,7 +21,8 @@ namespace command + + std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr); + +-static constexpr auto guidObjPath = "/org/openbmc/control/chassis0"; ++static constexpr auto guidObjPath = ++ "/xyz/openbmc_project/Chassis/Control/Chassis0"; + static constexpr auto propInterface = "org.freedesktop.DBus.Properties"; + + Guid getSystemGUID() +@@ -31,7 +32,7 @@ Guid getSystemGUID() + Guid guid = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}; + +- constexpr auto chassisIntf = "org.openbmc.control.Chassis"; ++ constexpr auto chassisIntf = "xyz.openbmc_project.Chassis.Control.Chassis"; + + sd_bus_message* reply = nullptr; + sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0007-Adding-support-for-GetSessionInfo-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0007-Adding-support-for-GetSessionInfo-command.patch new file mode 100644 index 000000000..fda7ed2ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0007-Adding-support-for-GetSessionInfo-command.patch @@ -0,0 +1,421 @@ +From f5c7d30be4a097998d9390614c0faa2d77109ca5 Mon Sep 17 00:00:00 2001 +From: ssekar <suryakanth.sekar@linux.intel.com> +Date: Wed, 12 Dec 2018 16:04:15 +0530 +Subject: [PATCH] Adding support for GetSessionInfo command + +Description: user can get all session info (remote ip,port, +session id, priv, etc) using this command. + +Verification :we can get all active and non active session +info by session handle session id. +Updated the Remote IP addr and Port update for sessioninfo. +Unit testing are done. + +Change-Id: I662ef2b9f0c1d6bda331eb6481d7b9f34534541b +Signed-off-by: ssekar <suryakanth.sekar@linux.intel.com> +--- + comm_module.cpp | 8 +++ + command/session_cmds.cpp | 147 +++++++++++++++++++++++++++++++++++++++ + command/session_cmds.hpp | 55 +++++++++++++++ + message_handler.cpp | 1 + + sessions_manager.cpp | 55 +++++++++++++++ + sessions_manager.hpp | 7 ++ + socket_channel.cpp | 27 ++++++- + socket_channel.hpp | 3 +- + 8 files changed, 301 insertions(+), 2 deletions(-) + +Index: phosphor-net-ipmid.clean/comm_module.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/comm_module.cpp ++++ phosphor-net-ipmid.clean/comm_module.cpp +@@ -53,6 +53,14 @@ void sessionSetupCommands() + &closeSession, + session::Privilege::CALLBACK, + false}, ++ // Session Info Command ++ { ++ { ++ (static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | ++ static_cast<uint16_t>(command::NetFns::APP) | 0x3D ++ }, ++ &getSessionInfo, session::Privilege::USER, false ++ }, + }; + + for (auto& iter : commands) +Index: phosphor-net-ipmid.clean/command/session_cmds.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/session_cmds.cpp ++++ phosphor-net-ipmid.clean/command/session_cmds.cpp +@@ -5,11 +5,19 @@ + + #include <host-ipmid/ipmid-api.h> + ++#include <iostream> + #include <user_channel/channel_layer.hpp> + #include <user_channel/user_layer.hpp> + + namespace command + { ++// Defined as per IPMI sepcification ++static constexpr uint8_t searchCurrentSession = 0x00; ++static constexpr uint8_t searchSessionByHandle = 0xFE; ++static constexpr uint8_t searchSessionByID = 0xFF; ++ ++static constexpr uint8_t ipmi15VerSession = 0x00; ++static constexpr uint8_t ipmi20VerSession = 0x01; + + std::vector<uint8_t> + setSessionPrivilegeLevel(const std::vector<uint8_t>& inPayload, +@@ -110,4 +118,143 @@ std::vector<uint8_t> closeSession(const + return outPayload; + } + ++std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, ++ const message::Handler& handler) ++ ++{ ++ std::vector<uint8_t> outPayload(sizeof(GetSessionInfoResponse)); ++ auto request = ++ reinterpret_cast<const GetSessionInfoRequest*>(inPayload.data()); ++ auto response = ++ reinterpret_cast<GetSessionInfoResponse*>(outPayload.data()); ++ uint32_t reqSessionID = handler.sessionID; ++ response->completionCode = IPMI_CC_OK; ++ if (inPayload.size() == 1 && request->sessionIndex != 0) ++ { ++ if (request->sessionIndex <= session::MAX_SESSION_COUNT) ++ { ++ reqSessionID = std::get<session::Manager&>(singletonPool) ++ .getSessionIDbyHandle(request->sessionIndex); ++ } ++ else ++ { ++ response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ } ++ ++ // Here we look for session info according to session index parameter ++ switch (request->sessionIndex) ++ { ++ // Look for current active session which this cmd is received over ++ case searchCurrentSession: ++ // Request data should only contain session index byte ++ if (inPayload.size() != 1) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ // To look for current active session which the command came over, ++ // the session ID cannot be 0. ++ if (0 == reqSessionID) ++ { ++ response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ break; ++ case searchSessionByHandle: ++ // Request data should only contain session index byte and Session ++ // handle ++ if (inPayload.size() != 2) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ ++ // Retrieve session id based on session handle ++ if (request->sessionHandle <= session::MAX_SESSION_COUNT) ++ { ++ reqSessionID = ++ std::get<session::Manager&>(singletonPool) ++ .getSessionIDbyHandle(request->sessionHandle); ++ } ++ else ++ { ++ response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ break; ++ case searchSessionByID: ++ // Request data should only contain session index byte and Session ++ // handle ++ if (inPayload.size() != sizeof(GetSessionInfoRequest)) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ reqSessionID = endian::from_ipmi(request->sessionID); ++ ++ break; ++ default: ++ if (inPayload.size() != 1) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ } ++ ++ response->totalSessionCount = session::MAX_SESSION_COUNT; ++ response->activeSessioncount = ++ std::get<session::Manager&>(singletonPool).getNoOfActiveSession(); ++ response->sessionHandle = 0; ++ if (reqSessionID != 0) ++ { ++ ++ std::shared_ptr<session::Session> sessionInfo; ++ try ++ { ++ sessionInfo = std::get<session::Manager&>(singletonPool) ++ .getSession(reqSessionID); ++ } ++ catch (std::exception& e) ++ { ++ response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ response->sessionHandle = std::get<session::Manager&>(singletonPool) ++ .getSessionHandle(reqSessionID); ++ uint8_t userId = ipmi::ipmiUserGetUserId(sessionInfo->userName); ++ if (userId == ipmi::invalidUserId) ++ { ++ response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ response->userID = userId; // userId; ++ response->privLevel = static_cast<uint8_t>(sessionInfo->curPrivLevel); ++ response->chanNum = sessionInfo->chNum; // byte7 3:0 ++ response->ipmiVer = ipmi20VerSession; // byte7 7:4 ++ response->remoteIpAddr = ++ sessionInfo->channelPtr->getRemoteAddressInbytes(); ++ response->remotePort = ++ sessionInfo->channelPtr->getPort(); // remoteSessionPort; ++ ++ std::cerr << "\nSessionInfo:" << (int)reqSessionID; ++ // TODO: Filling the Remote MACAddress ++ } ++ else ++ { ++ outPayload.resize(4); ++ } ++ return std::move(outPayload); ++} ++ + } // namespace command +Index: phosphor-net-ipmid.clean/command/session_cmds.hpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/session_cmds.hpp ++++ phosphor-net-ipmid.clean/command/session_cmds.hpp +@@ -116,4 +116,59 @@ struct CloseSessionResponse + std::vector<uint8_t> closeSession(const std::vector<uint8_t>& inPayload, + const message::Handler& handler); + ++/** ++ * @struct GetSessionInfoRequest ++ * ++ * IPMI Request data for getSession info command ++ */ ++struct GetSessionInfoRequest ++{ ++ uint8_t sessionIndex; ++ union ++ { ++ uint8_t sessionHandle; ++ uint32_t sessionID; ++ }; ++} __attribute__((packed)); ++ ++/** ++ * @struct getSessionInfoResponse ++ * ++ * IPMI Response data for getSession info command ++ */ ++struct GetSessionInfoResponse ++{ ++ uint8_t completionCode; ++ uint8_t sessionHandle; ++ uint8_t totalSessionCount; ++ uint8_t activeSessioncount; ++ uint8_t userID; ++ uint8_t privLevel; ++#if BYTE_ORDER == LITTLE_ENDIAN ++ uint8_t chanNum : 4; ++ uint8_t ipmiVer : 4; ++#endif ++#if BYTE_ORDER == BIG_ENDIAN ++ uint8_t ipmiVer : 4; ++ uint8_t chanNum : 4; ++#endif ++ uint32_t remoteIpAddr; // for channel private data ++ uint8_t remoteMACAddr[6]; ++ uint16_t remotePort; ++} __attribute__((packed)); ++ ++/** ++ * @brief GetSessionInfo Command ++ * ++ * This command is used to get the session information based on ++ * session handle or session ID. Retreive all session information. ++ ++ * @param[in] inPayload - Request Data for the command ++ * @param[in] handler - Reference to the Message Handler ++ * ++ * @return Response data for the command ++ */ ++std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, ++ const message::Handler& handler); ++ + } // namespace command +Index: phosphor-net-ipmid.clean/message_handler.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/message_handler.cpp ++++ phosphor-net-ipmid.clean/message_handler.cpp +@@ -43,6 +43,7 @@ std::shared_ptr<Message> Handler::receiv + sessionID = message->bmcSessionID; + message->rcSessionID = session->getRCSessionID(); + session->updateLastTransactionTime(); ++ session->channelPtr = channel; + + return message; + } +Index: phosphor-net-ipmid.clean/sessions_manager.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/sessions_manager.cpp ++++ phosphor-net-ipmid.clean/sessions_manager.cpp +@@ -88,6 +88,9 @@ std::shared_ptr<Session> + } + sessionID = session->getBMCSessionID(); + sessionsMap.emplace(sessionID, session); ++ storeSessionHandle(sessionID); ++ ++ + return session; + } + +@@ -149,12 +152,15 @@ std::shared_ptr<Session> Manager::getSes + + void Manager::cleanStaleEntries() + { ++ uint8_t sessionIndex = 0; + for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();) + { + auto session = iter->second; + if ((session->getBMCSessionID() != SESSION_ZERO) && + !(session->isSessionActive())) + { ++ sessionIndex = getSessionHandle(session->getBMCSessionID()); ++ sessionHandleMap[sessionIndex] = 0; + iter = sessionsMap.erase(iter); + } + else +@@ -164,4 +170,53 @@ void Manager::cleanStaleEntries() + } + } + ++uint8_t Manager::storeSessionHandle(SessionID bmcSessionID) ++{ ++ // Zero handler is reserved for invalid session. ++ //index starts with 1, for direct usage. Index 0 reserved ++ for (uint8_t i = 1; i <= MAX_SESSION_COUNT; i++) ++ { ++ if (sessionHandleMap[i] == 0) ++ { ++ sessionHandleMap[i] = bmcSessionID; ++ break; ++ } ++ } ++ return 0; ++} ++ ++uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const ++{ ++ if (sessionHandle <= MAX_SESSION_COUNT) ++ { ++ return sessionHandleMap[sessionHandle]; ++ } ++ return 0; ++} ++ ++uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const ++{ ++ ++ for (uint8_t i = 1; i <= MAX_SESSION_COUNT; i++) ++ { ++ if (sessionHandleMap[i] == bmcSessionID) ++ { ++ return i; ++ } ++ } ++ return 0; ++} ++uint8_t Manager::getNoOfActiveSession() const ++{ ++ uint8_t count = 0; ++ for (const auto& it : sessionsMap) ++ { ++ const auto& session = it.second; ++ if (session->state == State::ACTIVE) ++ { ++ count++; ++ } ++ } ++ return count; ++} + } // namespace session +Index: phosphor-net-ipmid.clean/sessions_manager.hpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/sessions_manager.hpp ++++ phosphor-net-ipmid.clean/sessions_manager.hpp +@@ -82,8 +82,15 @@ class Manager + std::shared_ptr<Session> + getSession(SessionID sessionID, + RetrieveOption option = RetrieveOption::BMC_SESSION_ID); ++ uint8_t getNoOfActiveSession() const; ++ uint8_t getSessionHandle(SessionID bmcSessionID) const; ++ uint8_t storeSessionHandle(SessionID bmcSessionID); ++ uint32_t getSessionIDbyHandle(uint8_t sessionHandle) const; + + private: ++ //+1 for session, as 0 is reserved for sessionless command ++ std::array<uint32_t, MAX_SESSION_COUNT + 1> sessionHandleMap; ++ + /** + * @brief Session Manager keeps the session objects as a sorted + * associative container with Session ID as the unique key +Index: phosphor-net-ipmid.clean/socket_channel.hpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/socket_channel.hpp ++++ phosphor-net-ipmid.clean/socket_channel.hpp +@@ -65,6 +65,23 @@ class Channel + } + + /** ++ * @brief Return the binary representation of the remote IPv4 address ++ * ++ * getSessionInfo needs to return the remote IPv4 addresses of each session ++ * ++ * @return A uint32_t representation of the remote IPv4 address ++ */ ++ std::uint32_t getRemoteAddressInbytes() ++ { ++ const boost::asio::ip::address& addr = endpoint.address(); ++ if (addr.is_v4()) ++ { ++ return addr.to_v4().to_uint(); ++ } ++ return 0; ++ } ++ ++ /** + * @brief Read the incoming packet + * + * Reads the data available on the socket diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch new file mode 100644 index 000000000..1a109a571 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch @@ -0,0 +1,318 @@ +From 0ecc7c816ad4836f8f54922ba92cb527f5978d5a Mon Sep 17 00:00:00 2001 +From: Suryakanth Sekar <suryakanth.sekar@linux.intel.com> +Date: Wed, 6 Mar 2019 10:35:56 +0530 +Subject: [PATCH] Sync GetSession Info cmd based on Upstream review + +Signed-off-by: Suryakanth Sekar <suryakanth.sekar@linux.intel.com> +--- + comm_module.cpp | 12 ++++---- + command/session_cmds.cpp | 72 +++++++++++++++++++++--------------------------- + sessions_manager.cpp | 10 +++---- + sessions_manager.hpp | 2 +- + socket_channel.hpp | 33 +++++++++++----------- + 5 files changed, 59 insertions(+), 70 deletions(-) + +diff --git a/comm_module.cpp b/comm_module.cpp +index 7a1a17d..2546583 100644 +--- a/comm_module.cpp ++++ b/comm_module.cpp +@@ -54,13 +54,11 @@ void sessionSetupCommands() + session::Privilege::CALLBACK, + false}, + // Session Info Command +- { +- { +- (static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | +- static_cast<uint16_t>(command::NetFns::APP) | 0x3D +- }, +- &getSessionInfo, session::Privilege::USER, false +- }, ++ {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | ++ static_cast<uint16_t>(command::NetFns::APP) | 0x3D}, ++ &getSessionInfo, ++ session::Privilege::USER, ++ false}, + }; + + for (auto& iter : commands) +diff --git a/command/session_cmds.cpp b/command/session_cmds.cpp +index 7563b18..fc996a4 100644 +--- a/command/session_cmds.cpp ++++ b/command/session_cmds.cpp +@@ -5,13 +5,12 @@ + + #include <ipmid/api.h> + +-#include <iostream> + #include <user_channel/channel_layer.hpp> + #include <user_channel/user_layer.hpp> + + namespace command + { +-// Defined as per IPMI sepcification ++// Defined as per IPMI specification + static constexpr uint8_t searchCurrentSession = 0x00; + static constexpr uint8_t searchSessionByHandle = 0xFE; + static constexpr uint8_t searchSessionByID = 0xFF; +@@ -129,20 +128,6 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + reinterpret_cast<GetSessionInfoResponse*>(outPayload.data()); + uint32_t reqSessionID = handler.sessionID; + response->completionCode = IPMI_CC_OK; +- if (inPayload.size() == 1 && request->sessionIndex != 0) +- { +- if (request->sessionIndex <= session::MAX_SESSION_COUNT) +- { +- reqSessionID = std::get<session::Manager&>(singletonPool) +- .getSessionIDbyHandle(request->sessionIndex); +- } +- else +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); +- } +- } + + // Here we look for session info according to session index parameter + switch (request->sessionIndex) +@@ -150,29 +135,22 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + // Look for current active session which this cmd is received over + case searchCurrentSession: + // Request data should only contain session index byte +- if (inPayload.size() != 1) ++ if (inPayload.size() != sizeof(request->sessionIndex)) + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); +- } +- // To look for current active session which the command came over, +- // the session ID cannot be 0. +- if (0 == reqSessionID) +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + break; + case searchSessionByHandle: + // Request data should only contain session index byte and Session + // handle +- if (inPayload.size() != 2) ++ if (inPayload.size() != (sizeof(request->sessionIndex) + ++ sizeof(request->sessionHandle))) + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + + // Retrieve session id based on session handle +@@ -186,7 +164,7 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + { + response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + break; + case searchSessionByID: +@@ -196,23 +174,38 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + reqSessionID = endian::from_ipmi(request->sessionID); + + break; + default: +- if (inPayload.size() != 1) ++ if (inPayload.size() == sizeof(request->sessionIndex)) ++ { ++ if (request->sessionIndex <= session::MAX_SESSION_COUNT) ++ { ++ reqSessionID = ++ std::get<session::Manager&>(singletonPool) ++ .getSessionIDbyHandle(request->sessionIndex); ++ } ++ else ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return outPayload; ++ } ++ } ++ else + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + } + + response->totalSessionCount = session::MAX_SESSION_COUNT; + response->activeSessioncount = +- std::get<session::Manager&>(singletonPool).getNoOfActiveSession(); ++ std::get<session::Manager&>(singletonPool).getActiveSessionCount(); + response->sessionHandle = 0; + if (reqSessionID != 0) + { +@@ -225,9 +218,9 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + } + catch (std::exception& e) + { +- response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + response->sessionHandle = std::get<session::Manager&>(singletonPool) + .getSessionHandle(reqSessionID); +@@ -236,25 +229,24 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + { + response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + response->userID = userId; // userId; + response->privLevel = static_cast<uint8_t>(sessionInfo->curPrivLevel); + response->chanNum = sessionInfo->chNum; // byte7 3:0 + response->ipmiVer = ipmi20VerSession; // byte7 7:4 +- response->remoteIpAddr = +- sessionInfo->channelPtr->getRemoteAddressInbytes(); + response->remotePort = + sessionInfo->channelPtr->getPort(); // remoteSessionPort; ++ response->remoteIpAddr = ++ sessionInfo->channelPtr->getRemoteAddressInBytes(); + +- std::cerr << "\nSessionInfo:" << (int)reqSessionID; + // TODO: Filling the Remote MACAddress + } + else + { + outPayload.resize(4); + } +- return std::move(outPayload); ++ return outPayload; + } + + } // namespace command +diff --git a/sessions_manager.cpp b/sessions_manager.cpp +index 9f3210b..c6897c6 100644 +--- a/sessions_manager.cpp ++++ b/sessions_manager.cpp +@@ -152,15 +152,13 @@ std::shared_ptr<Session> Manager::getSession(SessionID sessionID, + + void Manager::cleanStaleEntries() + { +- uint8_t sessionIndex = 0; + for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();) + { + auto session = iter->second; + if ((session->getBMCSessionID() != SESSION_ZERO) && + !(session->isSessionActive())) + { +- sessionIndex = getSessionHandle(session->getBMCSessionID()); +- sessionHandleMap[sessionIndex] = 0; ++ sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0; + iter = sessionsMap.erase(iter); + } + else +@@ -172,8 +170,8 @@ void Manager::cleanStaleEntries() + + uint8_t Manager::storeSessionHandle(SessionID bmcSessionID) + { +- // Zero handler is reserved for invalid session. +- //index starts with 1, for direct usage. Index 0 reserved ++ // Handler index 0 is reserved for invalid session. ++ // index starts with 1, for direct usage. Index 0 reserved + for (uint8_t i = 1; i <= MAX_SESSION_COUNT; i++) + { + if (sessionHandleMap[i] == 0) +@@ -206,7 +204,7 @@ uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const + } + return 0; + } +-uint8_t Manager::getNoOfActiveSession() const ++uint8_t Manager::getActiveSessionCount() const + { + uint8_t count = 0; + for (const auto& it : sessionsMap) +diff --git a/sessions_manager.hpp b/sessions_manager.hpp +index c4caad4..3a3825d 100644 +--- a/sessions_manager.hpp ++++ b/sessions_manager.hpp +@@ -82,7 +82,7 @@ class Manager + std::shared_ptr<Session> + getSession(SessionID sessionID, + RetrieveOption option = RetrieveOption::BMC_SESSION_ID); +- uint8_t getNoOfActiveSession() const; ++ uint8_t getActiveSessionCount() const; + uint8_t getSessionHandle(SessionID bmcSessionID) const; + uint8_t storeSessionHandle(SessionID bmcSessionID); + uint32_t getSessionIDbyHandle(uint8_t sessionHandle) const; +diff --git a/socket_channel.hpp b/socket_channel.hpp +index 349701e..8b64740 100644 +--- a/socket_channel.hpp ++++ b/socket_channel.hpp +@@ -52,33 +52,34 @@ class Channel + } + + /** +- * @brief Fetch the port number of the remote peer +- * +- * Returns the port number of the remote peer ++ * @brief Fetch the IP address of the remote peer + * +- * @return Port number ++ * Returns the IP address of the remote peer which is connected to this ++ * socket + * ++ * @return IP address of the remote peer + */ +- auto getPort() const ++ std::uint32_t getRemoteAddressInBytes() const + { +- return endpoint.port(); ++ const boost::asio::ip::address& addr = endpoint.address(); ++ if (addr.is_v4()) ++ { ++ return addr.to_v4().to_uint(); ++ } ++ return 0; + } + + /** +- * @brief Return the binary representation of the remote IPv4 address ++ * @brief Fetch the port number of the remote peer + * +- * getSessionInfo needs to return the remote IPv4 addresses of each session ++ * Returns the port number of the remote peer ++ * ++ * @return Port number + * +- * @return A uint32_t representation of the remote IPv4 address + */ +- std::uint32_t getRemoteAddressInbytes() ++ auto getPort() const + { +- const boost::asio::ip::address& addr = endpoint.address(); +- if (addr.is_v4()) +- { +- return addr.to_v4().to_uint(); +- } +- return 0; ++ return endpoint.port(); + } + + /** +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0009-Add-dbus-interface-for-sol-commands.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0009-Add-dbus-interface-for-sol-commands.patch new file mode 100644 index 000000000..771120120 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0009-Add-dbus-interface-for-sol-commands.patch @@ -0,0 +1,311 @@ +From 6b9aaf0304aed06e4b5ac53e7c163089568d4171 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Sat, 23 Mar 2019 04:03:07 +0800 +Subject: [PATCH] Add dbus interface for sol commands + +Add dbus interface for sol config parameters so that after move set/get +sol config parameter command from net-ipmid to host-ipmid, the command +can send config parameters to net-ipmid sol service through the dbus +interface. + +Tested by: +busctl introspect xyz.openbmc_project.Settings /xyz/openbmc_project +/network/host0/sol can show correct dbus properties of sol parameters. +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x00 0x01 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x01 0x00 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x02 0x83 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x03 0x5 0x03 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x04 0x5 0x03 +all these commands can change the dbus properties as the value in +above commands. +Before and after run these commands, ipmitool -I lanplus -H x -U x +-P x sol activate can start sol session correctly. +After reboot BMC, "Progress" property in dbus interface change back +to 0 and other properties will not reset to default value. + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +--- + command/payload_cmds.cpp | 1 + + command/sol_cmds.cpp | 84 --------------------------------- + sol/sol_manager.cpp | 119 +++++++++++++++++++++++++++++++++++++++++++++++ + sol/sol_manager.hpp | 1 + + sol_module.cpp | 6 --- + 5 files changed, 121 insertions(+), 90 deletions(-) + +diff --git a/command/payload_cmds.cpp b/command/payload_cmds.cpp +index 3b5b4f8..570cdff 100644 +--- a/command/payload_cmds.cpp ++++ b/command/payload_cmds.cpp +@@ -34,6 +34,7 @@ std::vector<uint8_t> activatePayload(const std::vector<uint8_t>& inPayload, + return outPayload; + } + ++ std::get<sol::Manager&>(singletonPool).updateSOLParameter(); + if (!std::get<sol::Manager&>(singletonPool).enable) + { + response->completionCode = IPMI_CC_PAYLOAD_TYPE_DISABLED; +diff --git a/command/sol_cmds.cpp b/command/sol_cmds.cpp +index a8fa410..804b5ea 100644 +--- a/command/sol_cmds.cpp ++++ b/command/sol_cmds.cpp +@@ -65,90 +65,6 @@ void activating(uint8_t payloadInstance, uint32_t sessionID) + outPayload); + } + +-std::vector<uint8_t> setConfParams(const std::vector<uint8_t>& inPayload, +- const message::Handler& handler) +-{ +- std::vector<uint8_t> outPayload(sizeof(SetConfParamsResponse)); +- auto request = +- reinterpret_cast<const SetConfParamsRequest*>(inPayload.data()); +- auto response = reinterpret_cast<SetConfParamsResponse*>(outPayload.data()); +- response->completionCode = IPMI_CC_OK; +- +- switch (static_cast<Parameter>(request->paramSelector)) +- { +- case Parameter::PROGRESS: +- { +- uint8_t progress = request->value & progressMask; +- std::get<sol::Manager&>(singletonPool).progress = progress; +- break; +- } +- case Parameter::ENABLE: +- { +- bool enable = request->value & enableMask; +- std::get<sol::Manager&>(singletonPool).enable = enable; +- break; +- } +- case Parameter::AUTHENTICATION: +- { +- if (!request->auth.auth || !request->auth.encrypt) +- { +- response->completionCode = ipmiCCWriteReadParameter; +- } +- else if (request->auth.privilege < +- static_cast<uint8_t>(session::Privilege::USER) || +- request->auth.privilege > +- static_cast<uint8_t>(session::Privilege::OEM)) +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- } +- else +- { +- std::get<sol::Manager&>(singletonPool).solMinPrivilege = +- static_cast<session::Privilege>(request->auth.privilege); +- } +- break; +- } +- case Parameter::ACCUMULATE: +- { +- using namespace std::chrono_literals; +- +- if (request->acc.threshold == 0) +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- break; +- } +- +- std::get<sol::Manager&>(singletonPool).accumulateInterval = +- request->acc.interval * sol::accIntervalFactor * 1ms; +- std::get<sol::Manager&>(singletonPool).sendThreshold = +- request->acc.threshold; +- break; +- } +- case Parameter::RETRY: +- { +- using namespace std::chrono_literals; +- +- std::get<sol::Manager&>(singletonPool).retryCount = +- request->retry.count; +- std::get<sol::Manager&>(singletonPool).retryInterval = +- request->retry.interval * sol::retryIntervalFactor * 1ms; +- break; +- } +- case Parameter::PORT: +- { +- response->completionCode = ipmiCCWriteReadParameter; +- break; +- } +- case Parameter::NVBITRATE: +- case Parameter::VBITRATE: +- case Parameter::CHANNEL: +- default: +- response->completionCode = ipmiCCParamNotSupported; +- } +- +- return outPayload; +-} +- + std::vector<uint8_t> getConfParams(const std::vector<uint8_t>& inPayload, + const message::Handler& handler) + { +diff --git a/sol/sol_manager.cpp b/sol/sol_manager.cpp +index 2046fe4..de36723 100644 +--- a/sol/sol_manager.cpp ++++ b/sol/sol_manager.cpp +@@ -12,7 +12,13 @@ + #include <boost/asio/write.hpp> + #include <chrono> + #include <cmath> ++#include <phosphor-ipmi-host/utils.hpp> + #include <phosphor-logging/log.hpp> ++#include <sdbusplus/message/types.hpp> ++ ++constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; ++constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol"; ++constexpr const char* PROP_INTF = "org.freedesktop.DBus.Properties"; + + namespace sol + { +@@ -93,6 +99,119 @@ void Manager::stopHostConsole() + } + } + ++std::string getService(sdbusplus::bus::bus& bus, const std::string& intf, ++ const std::string& path) ++{ ++ auto mapperCall = ++ bus.new_method_call("xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject"); ++ ++ mapperCall.append(path); ++ mapperCall.append(std::vector<std::string>({intf})); ++ ++ std::map<std::string, std::vector<std::string>> mapperResponse; ++ ++ try ++ { ++ auto mapperResponseMsg = bus.call(mapperCall); ++ mapperResponseMsg.read(mapperResponse); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ throw std::runtime_error("ERROR in mapper call"); ++ } ++ ++ if (mapperResponse.begin() == mapperResponse.end()) ++ { ++ throw std::runtime_error("ERROR in reading the mapper response"); ++ } ++ ++ return mapperResponse.begin()->first; ++} ++ ++ipmi::PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus, ++ const std::string& service, ++ const std::string& objPath, ++ const std::string& interface) ++{ ++ ipmi::PropertyMap properties; ++ ++ sdbusplus::message::message method = bus.new_method_call( ++ service.c_str(), objPath.c_str(), PROP_INTF, "GetAll"); ++ ++ method.append(interface); ++ ++ try ++ { ++ sdbusplus::message::message reply = bus.call(method); ++ reply.read(properties); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Failed to get all properties", ++ phosphor::logging::entry("PATH=%s", objPath.c_str()), ++ phosphor::logging::entry("INTERFACE=%s", interface.c_str())); ++ throw std::runtime_error("ERROR in reading proerties"); ++ } ++ ++ return properties; ++} ++ ++void Manager::updateSOLParameter() ++{ ++ std::variant<uint8_t, bool> value; ++ sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); ++ static std::string solService{}; ++ ipmi::PropertyMap properties; ++ if (solService.empty()) ++ { ++ try ++ { ++ solService = getService(dbus, solInterface, solPath); ++ } ++ catch (const std::runtime_error& e) ++ { ++ solService.clear(); ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error: get SOL service failed"); ++ return; ++ } ++ } ++ try ++ { ++ properties = ++ getAllDbusProperties(dbus, solService, solPath, solInterface); ++ } ++ catch (const std::runtime_error&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error setting sol parameter"); ++ return; ++ } ++ ++ progress = std::get<uint8_t>(properties["Progress"]); ++ ++ enable = std::get<bool>(properties["Enable"]); ++ ++ solMinPrivilege = static_cast<session::Privilege>( ++ std::get<uint8_t>(properties["Authentication"])); ++ ++ accumulateInterval = ++ std::get<uint8_t>((properties["AccumulateIntervalMS"])) * ++ sol::accIntervalFactor * 1ms; ++ ++ sendThreshold = std::get<uint8_t>(properties["Threshold"]); ++ ++ retryCount = std::get<uint8_t>(properties["RetryCount"]); ++ ++ retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) * ++ sol::retryIntervalFactor * 1ms; ++ ++ return; ++} ++ + void Manager::startPayloadInstance(uint8_t payloadInstance, + session::SessionID sessionID) + { +diff --git a/sol/sol_manager.hpp b/sol/sol_manager.hpp +index 5d96890..b7eb89e 100644 +--- a/sol/sol_manager.hpp ++++ b/sol/sol_manager.hpp +@@ -248,6 +248,7 @@ class Manager + * @return 0 on success and errno on failure. + */ + int writeConsoleSocket(const std::vector<uint8_t>& input) const; ++ void updateSOLParameter(void); + + private: + SOLPayloadMap payloadMap; +diff --git a/sol_module.cpp b/sol_module.cpp +index 8200e74..2b1fb46 100644 +--- a/sol_module.cpp ++++ b/sol_module.cpp +@@ -42,12 +42,6 @@ void registerCommands() + &getPayloadInfo, + session::Privilege::USER, + false}, +- // Set SOL Configuration Parameters +- {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | +- static_cast<uint16_t>(::command::NetFns::TRANSPORT) | 0x21}, +- &setConfParams, +- session::Privilege::ADMIN, +- false}, + // Get SOL Configuration Parameters + {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | + static_cast<uint16_t>(::command::NetFns::TRANSPORT) | 0x22}, +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend new file mode 100644 index 000000000..4b82cc21c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend @@ -0,0 +1,19 @@ +inherit useradd + +# TODO: This should be removed, once up-stream bump up +# issue is resolved +#SRC_URI += "git://github.com/openbmc/phosphor-net-ipmid" +SRCREV = "7e5d38d2fb51fc746624ff2f2b3701cea245a8fb" + +USERADD_PACKAGES = "${PN}" +# add a group called ipmi +GROUPADD_PARAM_${PN} = "ipmi " + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " file://0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch \ + file://0007-Adding-support-for-GetSessionInfo-command.patch \ + file://0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch \ + file://0009-Add-dbus-interface-for-sol-commands.patch \ + " + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend new file mode 100644 index 000000000..72d991c7e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb new file mode 100644 index 000000000..09d2f9c41 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb @@ -0,0 +1,19 @@ +SUMMARY = "Node Manager Proxy" +DESCRIPTION = "The Node Manager Proxy provides a simple interface for communicating \ +with Management Engine via IPMB" + +SRC_URI = "git://git@github.com/Intel-BMC/node-manager;protocol=ssh" +SRCREV = "81133dd32bd0337aec9e026d1c9c2788028c7bdd" +PV = "0.1+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SYSTEMD_SERVICE_${PN} = "node-manager-proxy.service" + +DEPENDS = "sdbusplus \ + phosphor-logging \ + boost" + +S = "${WORKDIR}/git/" +inherit cmake systemd diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb new file mode 100644 index 000000000..dd48df0c6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb @@ -0,0 +1,21 @@ +SUMMARY = "Phosphor LED Group Management for Intel" +PR = "r1" + +inherit native +inherit obmc-phosphor-utils + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +PROVIDES += "virtual/phosphor-led-manager-config-native" + +SRC_URI += "file://led.yaml" +S = "${WORKDIR}" + +# Overwrite the example led layout yaml file prior +# to building the phosphor-led-manager package +do_install() { + SRC=${S} + DEST=${D}${datadir}/phosphor-led-manager + install -D ${SRC}/led.yaml ${DEST}/led.yaml +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml new file mode 100755 index 000000000..7b193f85b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml @@ -0,0 +1,44 @@ +bmc_booted: + +power_on: + +status_ok: + status_green: + Action: 'On' + status_amber: + Action: 'Off' + +status_degraded: + status_green: + Action: 'Blink' + DutyOn: 50 + Period: 1000 + status_amber: + Action: 'Off' + +status_non_critical: + status_green: + Action: 'Off' + status_amber: + Action: 'Blink' + DutyOn: 50 + Period: 1000 + +status_critical: + status_green: + Action: 'Off' + status_amber: + Action: 'On' + +enclosure_identify: + identify: + Action: 'On' + +cpu0_fault: + cpu0fault: + Action: 'On' + +cpu1_fault: + cpu1fault: + Action: 'On' + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/logging/phosphor-logging_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/logging/phosphor-logging_%.bbappend new file mode 100644 index 000000000..b1f4c1ce5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/logging/phosphor-logging_%.bbappend @@ -0,0 +1,2 @@ +SRCREV = "30047bf9647215951ba5dfe21ceb3e58a1b405a4" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts.bbappend new file mode 100644 index 000000000..dc22b3c95 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts.bbappend @@ -0,0 +1,3 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI = "file://init"
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init new file mode 100755 index 000000000..c5b2eb040 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init @@ -0,0 +1,180 @@ +#!/bin/sh + +# Copyright 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# provide a couple of places in the RO root filesystem +# that can be made RW with an overlayfs + +# start with /proc and /tmp mounted +[ -e /proc/mounts ] || mount -t proc proc /proc +grep -q /tmp /proc/mounts || mount -t tmpfs -o rw,nosuid,nodev tmp /tmp +grep -q /sys /proc/mounts || mount -t sysfs -o rw,nosuid,nodev,noexec sys /sys + +# list of things that need to be rw at boot +NV_OVERLAYS="/etc /var /home" + +# place to mount the real ubifs backing store +RWFS_MNT=/tmp/.rwfs + +if grep -q "$RWFS_MNT" /proc/mounts; then + # quit - we have already run + exit 0 +fi +mkdir -p "$RWFS_MNT" + +mtd_by_name() { + local name="$1" + local mtd="/dev/$(grep "$name" /proc/mtd | cut -d : -f 1)" + echo "$mtd" +} + +mtdnum_by_name() { + local name="$1" + local mtdnum="$(grep "$name" /proc/mtd | cut -c 4)" + echo "$mtdnum" +} + +NV_MTD=rwfs +NV_MTD_DEV="$(mtd_by_name ${NV_MTD})" +NV_MTD_NUM="$(mtdnum_by_name ${NV_MTD})" + +nvrw() { + local p="$1" + mkdir -p "${RWFS_MNT}${p}" "${RWFS_MNT}${p}.work" + local mname=$(echo "rwnv${p}" | sed 's,/,,g') + local opts="lowerdir=${p},upperdir=${RWFS_MNT}${p},workdir=${RWFS_MNT}${p}.work" + mount -t overlay -o "$opts" "$mname" "$p" +} + +targetted_clean() { + local LOG_TAG="restore-defaults" + # Do not delete server certificates for the web server or ssh + echo "removing targetted contents:" + cd "${RWFS_MNT}/etc" + for file in *; do + case $file in + # The only files that stay are here: + CA|RestoreDefaultConfiguration|dropbear|sdr|server.pem);; + # All else get removed. + *) echo "remove $file" + rm -rf $file;; + esac + done + # nothing should be in the workdir, but clear it just in case + rm -rf "${RWFS_MNT}/etc.work" + + # Log files remaining - but not to stdout. + echo "Files remaining: $(ls)" + + # clean everything out of /var + rm -rf "${RWFS_MNT}/var" "${RWFS_MNT}/var.work" +} + +full_clean() { + local OVL='' + for OVL in $NV_OVERLAYS; do + rm -rf "${RWFS_MNT}${OVL}" "${RWFS_MNT}${OVL}.work" + done +} + +# check for full factory reset: if so, ubiformat $NV_MTD_DEV +bootflags="0x$(sed -n 's/^.*bootflags=\([0-9a-f]*\).*$/\1/p' /proc/cmdline)" +bootflags=$((bootflags + 0)) +let "restore_op = $bootflags & 0x3" +if [ $restore_op -eq 3 ]; then + ubiformat -y "$NV_MTD_DEV" +fi + +# attach a UBI device to the MTD device +NV_UBI_DEV="/dev/ubi${NV_MTD_NUM}" +if [ ! -e $NV_UBI_DEV ]; then + if ! ubiattach -m "$NV_MTD_NUM" -d "$NV_MTD_NUM"; then + # the attach failed, so format the MTD device and try again + echo "Warning! Failed to attach $NV_UBI_DEV to $NV_MTD_DEV." + echo "UBI-formatting $NV_MTD_DEV to attach again. Data on this device will be lost." + ubiformat -y "$NV_MTD_DEV" + ubiattach -m "$NV_MTD_NUM" -d "$NV_MTD_NUM" + fi +fi + +# make a UBI volume on the UBI device +NV_UBI_VOL="${NV_UBI_DEV}_0" +if [ ! -e $NV_UBI_VOL ]; then + ubimkvol "$NV_UBI_DEV" -N "$NV_MTD" -m +fi + +# mount a UBIFS on the UBI volume +mount -t ubifs "$NV_UBI_VOL" "$RWFS_MNT" + +if [ $restore_op -eq 1 ]; then + targetted_clean +elif [ $restore_op -eq 2 ]; then + full_clean +fi + +for FS in $NV_OVERLAYS; do + nvrw "$FS" +done + +# make sure that /etc/fw_env.config mirrors our current uboot environment +UENV_MTD_INFO=$(grep UENV /proc/mtd) +if [ -n "$UENV_MTD_INFO" ]; then + UENV_MTD_INFO=$(echo "$UENV_MTD_INFO" | sed 's,^\([^:]*\): \([0-9a-f]*\) \([0-9a-f]*\) .*,/dev/\1 0 0x\2 0x\3,') + if ! grep -q "^${UENV_MTD_INFO}$" /etc/fw_env.config; then + echo "${UENV_MTD_INFO}" > /etc/fw_env.config + echo "Updated fw_env.config" + fi +fi + +# work around bug where /etc/machine-id will be mounted with a temporary file +# if rootfs is read-only and the file is empty +MACHINE_ID=/etc/machine-id +if [ ! -s "$MACHINE_ID" ]; then + systemd-machine-id-setup +fi + +# mount persistent NV filesystem, where immortal settings live +if ! grep -q sofs /proc/mounts; then + mkdir -p /var/sofs + SOFS_MTD=sofs + SOFS_MTD_DEV="$(mtd_by_name ${SOFS_MTD})" + SOFS_MTD_NUM="$(mtdnum_by_name ${SOFS_MTD})" + SOFS_UBI_DEV="/dev/ubi${SOFS_MTD_NUM}" + + # attach a UBI device to the MTD device + if [ ! -e $SOFS_UBI_DEV ]; then + if ! ubiattach -m "$SOFS_MTD_NUM" -d "$SOFS_MTD_NUM"; then + # the attach failed, so format the MTD device and try again + echo "Warning! Failed to attach $SOFS_UBI_DEV to $SOFS_MTD_DEV." + echo "UBI-formatting $SOFS_MTD_DEV to attach again. Data on this device will be lost." + ubiformat -y "$SOFS_MTD_DEV" + ubiattach -m "$SOFS_MTD_NUM" -d "$SOFS_MTD_NUM" + fi + fi + + # make a UBI volume on the UBI device + SOFS_UBI_VOL="${SOFS_UBI_DEV}_0" + if [ ! -e $SOFS_UBI_VOL ]; then + ubimkvol "$SOFS_UBI_DEV" -N "$SOFS_MTD" -m + fi + + # mount a UBIFS on the UBI volume + mount -t ubifs "$SOFS_UBI_VOL" /var/sofs +fi + +echo "Finished mounting non-volatile overlays" + +exec /lib/systemd/systemd diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend new file mode 100644 index 000000000..809d05b94 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend @@ -0,0 +1,6 @@ +# Enable downstream autobump +SRC_URI = "git://github.com/openbmc/phosphor-sel-logger.git" +SRCREV = "2b9704d7eb666c945c73dd74a426a0af2292b0ea" + +# Enable threshold monitoring +EXTRA_OECMAKE += "-DSEL_LOGGER_MONITOR_THRESHOLD_EVENTS=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service new file mode 100644 index 000000000..b8c3554ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service @@ -0,0 +1,10 @@ +[Unit] +Description= BMC Self-Test + +[Service] +Restart=always +ExecStart=/usr/bin/env selftest +SyslogIdentifier=selftest + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test_git.bb new file mode 100644 index 000000000..da1d74207 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test_git.bb @@ -0,0 +1,38 @@ +SUMMARY = "BMC Self Test service" +DESCRIPTION = "BMC Self Test service for subsystem diagnosis failure info" + +SRC_URI = "git://git@github.com/Intel-BMC/intel-self-test;protocol=ssh" + +PV = "1.0+git${SRCPV}" +SRCREV = "d039998ad2c55aeae4191af30e15bbd3032508c1" + +S = "${WORKDIR}/git" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=fa818a259cbed7ce8bc2a22d35a464fc" + +inherit cmake +inherit obmc-phosphor-dbus-service +inherit obmc-phosphor-systemd +inherit pkgconfig pythonnative + +SYSTEMD_SERVICE_${PN} += "xyz.openbmc_project.selftest.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-logging \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + " + +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + phosphor-dbus-interfaces \ + " + +EXTRA_OECMAKE = " -DENABLE_GTEST=OFF -DCMAKE_SKIP_RPATH=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend new file mode 100644 index 000000000..dff1647e8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend @@ -0,0 +1,4 @@ +SRCREV = "209ec56cca898725213140fd0d1205a4d2f1c6a4" +SRC_URI = "git://github.com/openbmc/dbus-sensors.git" + +DEPENDS_append = " i2c-tools" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native.bbappend new file mode 100644 index 000000000..436623234 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://defaults.yaml \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native/defaults.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native/defaults.yaml new file mode 100644 index 000000000..24816fb4c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native/defaults.yaml @@ -0,0 +1,181 @@ +/xyz/openbmc_project/control/minimum_ship_level_required: + - Interface: xyz.openbmc_project.Control.MinimumShipLevel + Properties: + MinimumShipLevelRequired: + Default: 'true' + +/xyz/openbmc_project/control/host0/auto_reboot: + - Interface: xyz.openbmc_project.Control.Boot.RebootPolicy + Properties: + AutoReboot: + Default: 'false' + +/xyz/openbmc_project/control/host0/boot: + - Interface: xyz.openbmc_project.Control.Boot.Source + Properties: + BootSource: + Default: Source::Sources::Default + - Interface: xyz.openbmc_project.Control.Boot.Mode + Properties: + BootMode: + Default: Mode::Modes::Regular + +/xyz/openbmc_project/control/host0/boot/one_time: + - Interface: xyz.openbmc_project.Control.Boot.Source + Properties: + BootSource: + Default: Source::Sources::Default + - Interface: xyz.openbmc_project.Control.Boot.Mode + Properties: + BootMode: + Default: Mode::Modes::Regular + - Interface: xyz.openbmc_project.Object.Enable + Properties: + Enabled: + Default: 'true' + +/xyz/openbmc_project/control/host0/power_cap: + - Interface: xyz.openbmc_project.Control.Power.Cap + Properties: + PowerCap: + Default: 0 + Validation: + Type: "range" + Validator: "0..1000" + Unit: "Watts" + PowerCapEnable: + Default: 'false' + +/xyz/openbmc_project/control/host0/power_restore_policy: + - Interface: xyz.openbmc_project.Control.Power.RestorePolicy + Properties: + PowerRestorePolicy: + Default: RestorePolicy::Policy::AlwaysOff + +/xyz/openbmc_project/control/power_restore_delay: + - Interface: xyz.openbmc_project.Control.Power.RestoreDelay + Properties: + PowerRestoreDelay: + Default: 0 + +/xyz/openbmc_project/control/host0/acpi_power_state: + - Interface: xyz.openbmc_project.Control.Power.ACPIPowerState + Properties: + SysACPIStatus: + Default: ACPIPowerState::ACPI::Unknown + DevACPIStatus: + Default: ACPIPowerState::ACPI::Unknown + +/xyz/openbmc_project/time/owner: + - Interface: xyz.openbmc_project.Time.Owner + Properties: + TimeOwner: + Default: Owner::Owners::BMC + +/xyz/openbmc_project/time/sync_method: + - Interface: xyz.openbmc_project.Time.Synchronization + Properties: + TimeSyncMethod: + Default: Synchronization::Method::NTP + +/xyz/openbmc_project/network/host0/intf: + - Interface: xyz.openbmc_project.Network.MACAddress + Properties: + MACAddress: + Default: '"00:00:00:00:00:00"' + Validation: + Type: "regex" + Validator: '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$' + +#needs to implement address validation TODO openbmc/issues/2046 +/xyz/openbmc_project/network/host0/intf/addr: + - Interface: xyz.openbmc_project.Network.IP + Properties: + Address: + Default: '"0.0.0.0"' + PrefixLength: + Default: 0 + Validation: + Type: "range" + Validator: 0..128 + Unit: "bits" + Origin: + Default: IP::AddressOrigin::Static + Gateway: + Default: '"0.0.0.0"' + Type: + Default: IP::Protocol::IPv4 + +/xyz/openbmc_project/control/host0/restriction_mode: + - Interface: xyz.openbmc_project.Control.Security.RestrictionMode + Properties: + RestrictionMode: + Default: RestrictionMode::Modes::None + +/xyz/openbmc_project/control/host0/TPMEnable: + - Interface: xyz.openbmc_project.Control.TPM.Policy + Properties: + TPMEnable: + Default: 'false' + +/xyz/openbmc_project/control/power_supply_redundancy: + - Interface: xyz.openbmc_project.Control.PowerSupplyRedundancy + Properties: + PowerSupplyRedundancyEnabled: + Default: 'true' + +/xyz/openbmc_project/control/host0/turbo_allowed: + - Interface: xyz.openbmc_project.Control.Host.TurboAllowed + Properties: + TurboAllowed: + Default: 'true' + +/xyz/openbmc_project/control/host0/systemGUID: + - Interface: xyz.openbmc_project.Common.UUID + Properties: + UUID: + Default: '"00000000-0000-0000-0000-000000000000"' + +/xyz/openbmc_project/bios: + - Interface: xyz.openbmc_project.Inventory.Item.Bios + Properties: + BiosId: + Default: '"NA"' + +/xyz/openbmc_project/control/processor_error_config: + - Interface: xyz.openbmc_project.Control.Processor.ErrConfig + Properties: + ResetCfg: + Default: 0 + ResetErrorOccurrenceCounts: + Default: 0 + +/xyz/openbmc_project/control/shutdown_policy_config: + - Interface: xyz.openbmc_project.Control.ShutdownPolicy + Properties: + Policy: + Default: 0 + +/xyz/openbmc_project/control/chassis_capabilities_config: + - Interface: xyz.openbmc_project.Control.ChassisCapabilities + Properties: + CapabilitiesFlags: + Default: 0 + FRUDeviceAddress: + Default: 0x20 + SDRDeviceAddress: + Default: 0x20 + SELDeviceAddress: + Default: 0x20 + SMDeviceAddress: + Default: 0x20 + BridgeDeviceAddress: + Default: 0x20 + +/xyz/openbmc_project/control/thermal_mode: + - Interface: xyz.openbmc_project.Control.ThermalMode + Properties: + Current: + Default: '"Performance"' + Supported: + Default: '{"Acoustic", "Performance"}' diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager/0001-settings-initialize-data-file-with-default-setting.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager/0001-settings-initialize-data-file-with-default-setting.patch new file mode 100644 index 000000000..fcf2415d6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager/0001-settings-initialize-data-file-with-default-setting.patch @@ -0,0 +1,37 @@ +From 9e99aa4f72f4420e03ec2e4a29816eae43c5e748 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 29 May 2018 16:16:06 +0800 +Subject: [PATCH] [settings] initialize data file with default setting + +Current code trys to load settings from file at startup. When file +does not exist, it will just use default setting. However, it will +still load default on next reboot because no one create files. + +This change creates file as well when daemon loads default so next +time daemon could load/save from file. + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + settings_manager.mako.hpp | 3 +++ + 1 file changed, 3 insertions(+) + mode change 100644 => 100755 settings_manager.mako.hpp + +diff --git a/settings_manager.mako.hpp b/settings_manager.mako.hpp +old mode 100644 +new mode 100755 +index 09a5a1f..cd592a0 +--- a/settings_manager.mako.hpp ++++ b/settings_manager.mako.hpp +@@ -323,6 +323,9 @@ class Manager + else + { + initSetting${index}(); ++ std::ofstream ostr(path.c_str(), std::ios::out); ++ cereal::JSONOutputArchive oarchive(ostr); ++ oarchive(*std::get<${index}>(settings)); //create file with default + } + } + catch (cereal::Exception& e) +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager_%.bbappend new file mode 100644 index 000000000..bc695abe8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager_%.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0001-settings-initialize-data-file-with-default-setting.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_git.bb new file mode 100644 index 000000000..01c93490b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_git.bb @@ -0,0 +1,28 @@ +SUMMARY = "Service configuration manager daemon to control service properties" +DESCRIPTION = "Daemon controls service properies like port, channels, state etc.." + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git/srvcfg-manager" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "f4d4bfc3296cb27feb17aa5d1d93b3061b56ce10" + +inherit cmake systemd +SYSTEMD_SERVICE_${PN} = "srvcfg-manager.service" + +DEPENDS += " \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-logging \ + boost \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0001-Modify-dbus-interface-for-power-control.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0001-Modify-dbus-interface-for-power-control.patch new file mode 100644 index 000000000..fac9b52f1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0001-Modify-dbus-interface-for-power-control.patch @@ -0,0 +1,38 @@ +From d34a2a5f6ca0564275ed0e2664624525cad64585 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 13 Jul 2018 09:08:52 +0800 +Subject: [PATCH] Modify dbus interface for power control +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Switch power control service namespace from “org” to “xyz”, +to compatible with new intel-chassis services + +Change-Id: I1bf5e218f72eb9fd4fb6f203c35479818d12b1fa +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassis_state_manager.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/chassis_state_manager.cpp b/chassis_state_manager.cpp +index 03dd176..05e2440 100644 +--- a/chassis_state_manager.cpp ++++ b/chassis_state_manager.cpp +@@ -63,10 +63,11 @@ void Chassis::determineInitialState() + { + sdbusplus::message::variant<int> pgood = -1; + auto method = this->bus.new_method_call( +- "org.openbmc.control.Power", "/org/openbmc/control/power0", ++ "xyz.openbmc_project.Chassis.Control.Power", ++ "/xyz/openbmc_project/Chassis/Control/Power0", + "org.freedesktop.DBus.Properties", "Get"); + +- method.append("org.openbmc.control.Power", "pgood"); ++ method.append("xyz.openbmc_project.Chassis.Control.Power", "pgood"); + try + { + auto reply = this->bus.call(method); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0002-Capture-host-restart-cause.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0002-Capture-host-restart-cause.patch new file mode 100644 index 000000000..7d70b29fa --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0002-Capture-host-restart-cause.patch @@ -0,0 +1,234 @@ +From c0f01261572cb527cf9dc62fa732b28c658ff013 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 7 Aug 2018 16:43:00 +0800 +Subject: [PATCH] Capture host restart cause + +Capture host restart cause on power/reset button pressed, +and power restore policy settings. +Save the restart cause into file system. +And restort it when BMC boot up. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + configure.ac | 4 ++-- + discover_system_state.cpp | 14 ++++++++++++ + host_state_manager.cpp | 16 ++++++++++++++ + host_state_manager.hpp | 56 ++++++++++++++++++++++++++++++++++++++++++++--- + 4 files changed, 85 insertions(+), 5 deletions(-) + +diff --git a/configure.ac b/configure.ac +index e985a95..b9e64c8 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -56,9 +56,9 @@ AC_ARG_VAR(HOST_RUNNING_FILE, [File to create if host is running]) + AS_IF([test "x$HOST_RUNNING_FILE" == "x"], [HOST_RUNNING_FILE="/run/openbmc/host@%u-on"]) + AC_DEFINE_UNQUOTED([HOST_RUNNING_FILE], ["$HOST_RUNNING_FILE"], [File to create if host is running]) + +-AC_ARG_VAR(HOST_STATE_PERSIST_PATH, [Path of file for storing requested host state.]) ++AC_ARG_VAR(HOST_STATE_PERSIST_PATH, [Path of file for storing host state.]) + AS_IF([test "x$HOST_STATE_PERSIST_PATH" == "x"], \ +- [HOST_STATE_PERSIST_PATH="/var/lib/phosphor-state-manager/requestedHostTransition"]) ++ [HOST_STATE_PERSIST_PATH="/var/lib/phosphor-state-manager/hostState"]) + AC_DEFINE_UNQUOTED([HOST_STATE_PERSIST_PATH], ["$HOST_STATE_PERSIST_PATH"], \ + [Path of file for storing requested host state.]) + +diff --git a/discover_system_state.cpp b/discover_system_state.cpp +index 3a38152..0b5798a 100644 +--- a/discover_system_state.cpp ++++ b/discover_system_state.cpp +@@ -12,6 +12,7 @@ + #include "settings.hpp" + #include "xyz/openbmc_project/Common/error.hpp" + #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp" ++#include <xyz/openbmc_project/State/Host/server.hpp> + + namespace phosphor + { +@@ -181,6 +182,10 @@ int main(int argc, char** argv) + log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on"); + setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", + convertForMessage(server::Host::Transition::On)); ++ ++ setProperty( ++ bus, hostPath, HOST_BUSNAME, "HostRestartCause", ++ convertForMessage(server::Host::RestartCause::PowerPolicyAlwaysOn)); + } + else if (RestorePolicy::Policy::Restore == + RestorePolicy::convertPolicyFromString(powerPolicy)) +@@ -192,6 +197,15 @@ int main(int argc, char** argv) + getProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition"); + setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition", + hostReqState); ++ ++ if (server::Host::convertTransitionFromString(hostReqState) == ++ server::Host::Transition::On) ++ { ++ setProperty( ++ bus, hostPath, HOST_BUSNAME, "HostRestartCause", ++ convertForMessage( ++ server::Host::RestartCause::PowerPolicyPreviousState)); ++ } + } + + return 0; +diff --git a/host_state_manager.cpp b/host_state_manager.cpp +index 7d661dd..03d210d 100644 +--- a/host_state_manager.cpp ++++ b/host_state_manager.cpp +@@ -308,6 +308,15 @@ bool Host::deserialize(const fs::path& path) + } + } + ++void Host::restoreHostRestartCause() ++{ ++ if (!deserialize(HOST_STATE_PERSIST_PATH)) ++ { ++ // set to default value ++ server::Host::hostRestartCause(server::Host::RestartCause::Unknown); ++ } ++} ++ + Host::Transition Host::requestedHostTransition(Transition value) + { + log<level::INFO>("Host State transaction request", +@@ -353,6 +362,13 @@ Host::HostState Host::currentHostState(HostState value) + return server::Host::currentHostState(value); + } + ++Host::RestartCause Host::hostRestartCause(RestartCause value) ++{ ++ auto retVal = server::Host::hostRestartCause(value); ++ serialize(); ++ return retVal; ++} ++ + } // namespace manager + } // namespace state + } // namespace phosphor +diff --git a/host_state_manager.hpp b/host_state_manager.hpp +index 2b00777..e74fab7 100644 +--- a/host_state_manager.hpp ++++ b/host_state_manager.hpp +@@ -32,6 +32,15 @@ using namespace phosphor::logging; + namespace sdbusRule = sdbusplus::bus::match::rules; + namespace fs = std::experimental::filesystem; + ++const static constexpr char* powerButtonPath = ++ "/xyz/openbmc_project/Chassis/Buttons/Power0"; ++const static constexpr char* powerButtonIntf = ++ "xyz.openbmc_project.Chassis.Buttons.Power"; ++const static constexpr char* resetButtonPath = ++ "/xyz/openbmc_project/Chassis/Buttons/Reset0"; ++const static constexpr char* resetButtonIntf = ++ "xyz.openbmc_project.Chassis.Buttons.Reset"; ++ + /** @class Host + * @brief OpenBMC host state management implementation. + * @details A concrete implementation for xyz.openbmc_project.State.Host +@@ -59,7 +68,31 @@ class Host : public HostInherit + sdbusRule::interface("org.freedesktop.systemd1.Manager"), + std::bind(std::mem_fn(&Host::sysStateChange), this, + std::placeholders::_1)), +- settings(bus) ++ settings(bus), ++ powerButtonPressedSignal( ++ bus, ++ sdbusRule::type::signal() + sdbusRule::member("Pressed") + ++ sdbusRule::path(powerButtonPath) + ++ sdbusRule::interface(powerButtonIntf), ++ [this](sdbusplus::message::message &msg) { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "powerButtonPressedSignal callback function is called..."); ++ this->hostRestartCause(this->RestartCause::PowerButton); ++ return; ++ } ++ ), ++ resetButtonPressedSignal( ++ bus, ++ sdbusRule::type::signal() + sdbusRule::member("Pressed") + ++ sdbusRule::path(resetButtonPath) + ++ sdbusRule::interface(resetButtonIntf), ++ [this](sdbusplus::message::message &msg) { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "resetButtonPressedSignal callback function is called..."); ++ this->hostRestartCause(this->RestartCause::ResetButton); ++ return; ++ } ++ ) + { + // Enable systemd signals + subscribeToSystemdSignals(); +@@ -69,6 +102,8 @@ class Host : public HostInherit + + attemptsLeft(BOOT_COUNT_MAX_ALLOWED); + ++ restoreHostRestartCause(); // restore host restart cause from persisted file ++ + // We deferred this until we could get our property correct + this->emit_object_added(); + } +@@ -85,6 +120,9 @@ class Host : public HostInherit + /** @brief Set value of CurrentHostState */ + HostState currentHostState(HostState value) override; + ++ /** @brief Set value of HostRestartCause */ ++ RestartCause hostRestartCause(RestartCause value) override; ++ + /** + * @brief Set host reboot count to default + * +@@ -192,7 +230,10 @@ class Host : public HostInherit + server::Progress::bootProgress()), + convertForMessage( + sdbusplus::xyz::openbmc_project::State::OperatingSystem:: +- server::Status::operatingSystemState())); ++ server::Status::operatingSystemState()), ++ convertForMessage(sdbusplus::xyz::openbmc_project::State:: ++ server::Host::hostRestartCause()) ++ ); + } + + /** @brief Function required by Cereal to perform deserialization. +@@ -208,7 +249,8 @@ class Host : public HostInherit + std::string reqTranState; + std::string bootProgress; + std::string osState; +- archive(reqTranState, bootProgress, osState); ++ std::string restartCause; ++ archive(reqTranState, bootProgress, osState, restartCause); + auto reqTran = Host::convertTransitionFromString(reqTranState); + // When restoring, set the requested state with persistent value + // but don't call the override which would execute it +@@ -219,6 +261,8 @@ class Host : public HostInherit + sdbusplus::xyz::openbmc_project::State::OperatingSystem::server:: + Status::operatingSystemState( + Host::convertOSStatusFromString(osState)); ++ sdbusplus::xyz::openbmc_project::State::server::Host:: ++ hostRestartCause(Host::convertRestartCauseFromString(restartCause)); + } + + /** @brief Serialize and persist requested host state +@@ -239,6 +283,9 @@ class Host : public HostInherit + */ + bool deserialize(const fs::path& path); + ++ /** @brief Used to restore HostRestartCause value from persisted file */ ++ void restoreHostRestartCause(); ++ + /** @brief Persistent sdbusplus DBus bus connection. */ + sdbusplus::bus::bus& bus; + +@@ -247,6 +294,9 @@ class Host : public HostInherit + + // Settings objects of interest + settings::Objects settings; ++ ++ sdbusplus::bus::match_t powerButtonPressedSignal; ++ sdbusplus::bus::match_t resetButtonPressedSignal; + }; + + } // namespace manager +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0003-Use-warm-reboot-for-the-Reboot-host-state-transition.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0003-Use-warm-reboot-for-the-Reboot-host-state-transition.patch new file mode 100644 index 000000000..40722c3c9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0003-Use-warm-reboot-for-the-Reboot-host-state-transition.patch @@ -0,0 +1,71 @@ +From 8a7f73a0688524c71023da89e8cb5578dac16f5d Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 8 Mar 2019 17:21:49 +0800 +Subject: [PATCH] Use warm-reboot for the Reboot host state transition + +The same as reset button pressing, the pgood signal will keep on during the reboot + +Tested By: +ipmitool chassis power reset +The pgood(gpio219) will be keep high during the reset process + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + host_state_manager.cpp | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/host_state_manager.cpp b/host_state_manager.cpp +index 03d210d..8fac348 100644 +--- a/host_state_manager.cpp ++++ b/host_state_manager.cpp +@@ -44,8 +44,9 @@ constexpr auto HOST_STATE_SOFT_POWEROFF_TGT = "obmc-host-shutdown@0.target"; + constexpr auto HOST_STATE_POWEROFF_TGT = "obmc-host-stop@0.target"; + constexpr auto HOST_STATE_POWERON_TGT = "obmc-host-start@0.target"; + constexpr auto HOST_STATE_POWERON_MIN_TGT = "obmc-host-startmin@0.target"; +-constexpr auto HOST_STATE_REBOOT_TGT = "obmc-host-reboot@0.target"; ++constexpr auto HOST_STATE_REBOOT_TGT = "obmc-host-warm-reset@0.target"; + constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target"; ++constexpr auto hostStateRebootService = "intel-power-warm-reset@0.service"; + + constexpr auto ACTIVE_STATE = "active"; + constexpr auto ACTIVATING_STATE = "activating"; +@@ -168,6 +169,7 @@ bool Host::stateActive(const std::string& target) + + const auto& currentStateStr = + sdbusplus::message::variant_ns::get<std::string>(currentState); ++ + return currentStateStr == ACTIVE_STATE || + currentStateStr == ACTIVATING_STATE; + } +@@ -236,18 +238,22 @@ void Host::sysStateChange(sdbusplus::message::message& msg) + // Read the msg and populate each variable + msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); + +- if ((newStateUnit == HOST_STATE_POWEROFF_TGT) && +- (newStateResult == "done") && +- (!stateActive(HOST_STATE_POWERON_MIN_TGT))) ++ if (((newStateUnit == HOST_STATE_POWEROFF_TGT) && ++ (newStateResult == "done") && ++ (!stateActive(HOST_STATE_POWERON_MIN_TGT))) || ++ ((newStateUnit == hostStateRebootService) && ++ (newStateResult == "done"))) + { + log<level::INFO>("Received signal that host is off"); + this->currentHostState(server::Host::HostState::Off); + this->bootProgress(bootprogress::Progress::ProgressStages::Unspecified); + this->operatingSystemState(osstatus::Status::OSStatus::Inactive); + } +- else if ((newStateUnit == HOST_STATE_POWERON_MIN_TGT) && +- (newStateResult == "done") && +- (stateActive(HOST_STATE_POWERON_MIN_TGT))) ++ else if (((newStateUnit == HOST_STATE_POWERON_MIN_TGT) && ++ (newStateResult == "done") && ++ (stateActive(HOST_STATE_POWERON_MIN_TGT))) || ++ ((newStateUnit == HOST_STATE_REBOOT_TGT) && ++ (newStateResult == "done"))) + { + log<level::INFO>("Received signal that host is running"); + this->currentHostState(server::Host::HostState::Running); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reboot-host@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reboot-host@.service new file mode 100644 index 000000000..ffde01ca3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reboot-host@.service @@ -0,0 +1,18 @@ +[Unit] +Description=Reboot host%i +Wants=obmc-host-stop@%i.target +After=obmc-host-stop@%i.target + +[Service] +#ExecStart={base_bindir}/systemctl start obmc-host-start@%i.target +# This service is starting another target that conflicts with the +# target this service is running in. OpenBMC needs a refactor of +# how it does its host reset path. Until then, this short term +# solution does the job. +# Since this is a part of the reboot target, call the startmin +# target which does the minimum required to start the host. +ExecStart=/bin/sh -c "sleep 10 && systemctl start obmc-host-startmin@%i.target" + + +[Install] +WantedBy=obmc-host-reboot@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-check@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-check@.service new file mode 100644 index 000000000..13b8f0fca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-check@.service @@ -0,0 +1,19 @@ +[Unit] +Description=Check Host%i status on BMC reset +Wants=mapper-wait@-xyz-openbmc_project-control-host%i.service +After=mapper-wait@-xyz-openbmc_project-control-host%i.service +Wants=obmc-host-reset-running@%i.target +Before=obmc-host-reset-running@%i.target +Wants=op-reset-chassis-on@%i.service +After=op-reset-chassis-on@%i.service +Conflicts=obmc-host-stop@%i.target +ConditionPathExists=/run/openbmc/chassis@%i-on + +[Service] +RemainAfterExit=yes +Type=oneshot +ExecStart=/bin/sh -c "if [ $(busctl get-property `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power vrd_good | sed 's/i\s*[1]/on/' | grep on | wc -l) != 0 ]; then mkdir -p /run/openbmc/ && touch /run/openbmc/host@%i-on; fi" +SyslogIdentifier=phosphor-host-check + +[Install] +WantedBy=obmc-host-reset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-reboot-attempts@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-reboot-attempts@.service new file mode 100644 index 000000000..87c750c57 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-reboot-attempts@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Reset host reboot counter +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service +ConditionPathExists=!/run/openbmc/host@%i-on + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "busctl set-property `mapper get-service /xyz/openbmc_project/state/host%i` /xyz/openbmc_project/state/host%i xyz.openbmc_project.Control.Boot.RebootAttempts AttemptsLeft u 3" +StartLimitInterval=0 + +[Install] +WantedBy=obmc-host-start@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend new file mode 100644 index 000000000..4c50ecf5e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0001-Modify-dbus-interface-for-power-control.patch \ + file://phosphor-reboot-host@.service \ + file://phosphor-reset-host-reboot-attempts@.service \ + file://phosphor-reset-host-check@.service \ + file://0002-Capture-host-restart-cause.patch \ + file://0003-Use-warm-reboot-for-the-Reboot-host-state-transition.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/post-code-manager/0001-Implement-post-code-manager.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/state/post-code-manager/0001-Implement-post-code-manager.patch new file mode 100644 index 000000000..8915484cd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/post-code-manager/0001-Implement-post-code-manager.patch @@ -0,0 +1,499 @@ +From 7d78e70735e1bce51ef34cfe128be68758de3447 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 19 Feb 2019 15:00:11 +0800 +Subject: [PATCH] Implement post code manager + +Implement method and properties defined in PostCode.interface.yaml +under phosphor-dbus-interfaces/xyz/openbmc_project/State/Boot +1. Method: std::vector<uint64_t> PostCode::getPostCodes(uint16_t index) +2. Properties: CurrentBootCycleIndex/MaxBootCycleNum + +Test-By: + Every cycle post codes is saved in "/var/lib/phosphor-post-code-manager" + "1" file is saved all post codes for cycle 1 + "2" file is saved all post codes for cycle 2 + "CurrentBootCycleIndex" file is saved the current boot cycle number. + root@wolfpass:/var/lib/phosphor-post-code-manager# ls + 1 2 CurrentBootCycleIndex + +Change-Id: Ia89b9121983261fef5573092d890beb84626ceeb +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + CMakeLists.txt | 45 ++++++ + MAINTAINERS | 45 ++++++ + inc/post_code.hpp | 152 ++++++++++++++++++ + ...penbmc_project.State.Boot.PostCode.service | 11 ++ + src/main.cpp | 61 +++++++ + src/post_code.cpp | 109 +++++++++++++ + 6 files changed, 423 insertions(+) + create mode 100644 CMakeLists.txt + create mode 100644 MAINTAINERS + create mode 100644 inc/post_code.hpp + create mode 100644 service_files/xyz.openbmc_project.State.Boot.PostCode.service + create mode 100644 src/main.cpp + create mode 100644 src/post_code.cpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +new file mode 100644 +index 0000000..594d839 +--- /dev/null ++++ b/CMakeLists.txt +@@ -0,0 +1,45 @@ ++cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) ++project(post-code-manager CXX) ++set(CMAKE_CXX_STANDARD 17) ++set(CMAKE_CXX_STANDARD_REQUIRED ON) ++ ++set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) ++include(GNUInstallDirs) ++include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc) ++include_directories(${CMAKE_CURRENT_BINARY_DIR}) ++ ++set(DBUS_OBJECT_NAME "xyz/openbmc_project/State/Boot/PostCode") ++set(DBUS_INTF_NAME "xyz.openbmc_project.State.Boot.PostCode") ++ ++add_definitions(-DDBUS_OBJECT_NAME="/${DBUS_OBJECT_NAME}") ++add_definitions(-DDBUS_INTF_NAME="${DBUS_INTF_NAME}") ++set(SRC_FILES src/post_code.cpp ++ src/main.cpp ) ++set ( SERVICE_FILES ++ service_files/xyz.openbmc_project.State.Boot.PostCode.service ) ++ ++# import sdbusplus ++find_package(PkgConfig REQUIRED) ++pkg_check_modules(SDBUSPLUSPLUS sdbusplus REQUIRED) ++include_directories(${SDBUSPLUSPLUS_INCLUDE_DIRS}) ++link_directories(${SDBUSPLUSPLUS_LIBRARY_DIRS}) ++find_program(SDBUSPLUSPLUS sdbus++) ++ ++# import phosphor-logging ++find_package(PkgConfig REQUIRED) ++pkg_check_modules(LOGGING phosphor-logging REQUIRED) ++include_directories(${LOGGING_INCLUDE_DIRS}) ++link_directories(${LOGGING_LIBRARY_DIRS}) ++ ++# phosphor-dbus-interfaces ++find_package(PkgConfig REQUIRED) ++pkg_check_modules(DBUSINTERFACE phosphor-dbus-interfaces REQUIRED) ++include_directories(${DBUSINTERFACE_INCLUDE_DIRS}) ++link_directories(${DBUSINTERFACE_LIBRARY_DIRS}) ++ ++add_executable(${PROJECT_NAME} ${SRC_FILES}) ++target_link_libraries(${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES} ) ++target_link_libraries(${PROJECT_NAME} "${SDBUSPLUSPLUS_LIBRARIES} -lstdc++fs -lphosphor_dbus") ++ ++install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) ++install (FILES ${SERVICE_FILES} DESTINATION /lib/systemd/system/) +\ No newline at end of file +diff --git a/MAINTAINERS b/MAINTAINERS +new file mode 100644 +index 0000000..de6cc54 +--- /dev/null ++++ b/MAINTAINERS +@@ -0,0 +1,45 @@ ++How to use this list: ++ Find the most specific section entry (described below) that matches where ++ your change lives and add the reviewers (R) and maintainers (M) as ++ reviewers. You can use the same method to track down who knows a particular ++ code base best. ++ ++ Your change/query may span multiple entries; that is okay. ++ ++ If you do not find an entry that describes your request at all, someone ++ forgot to update this list; please at least file an issue or send an email ++ to a maintainer, but preferably you should just update this document. ++ ++Description of section entries: ++ ++ Section entries are structured according to the following scheme: ++ ++ X: NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!> ++ X: ... ++ . ++ . ++ . ++ ++ Where REPO_NAME is the name of the repository within the OpenBMC GitHub ++ organization; FILE_PATH is a file path within the repository, possibly with ++ wildcards; X is a tag of one of the following types: ++ ++ M: Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>; ++ if omitted from an entry, assume one of the maintainers from the ++ MAINTAINERS entry. ++ R: Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>; ++ these people are to be added as reviewers for a change matching the repo ++ path. ++ F: Denotes forked from an external repository; has fields URL. ++ ++ Line comments are to be denoted "# SOME COMMENT" (typical shell style ++ comment); it is important to follow the correct syntax and semantics as we ++ may want to use automated tools with this file in the future. ++ ++ A change cannot be added to an OpenBMC repository without a MAINTAINER's ++ approval; thus, a MAINTAINER should always be listed as a reviewer. ++ ++START OF MAINTAINERS LIST ++------------------------- ++ ++M: Kuiying Wang <kuiying.wang@intel.com> <kuiyingw> +\ No newline at end of file +diff --git a/inc/post_code.hpp b/inc/post_code.hpp +new file mode 100644 +index 0000000..84c8b3e +--- /dev/null ++++ b/inc/post_code.hpp +@@ -0,0 +1,152 @@ ++/* ++// Copyright (c) 2019 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++#pragma once ++#include <fcntl.h> ++#include <unistd.h> ++#include <cereal/cereal.hpp> ++#include <experimental/filesystem> ++#include <cereal/access.hpp> ++#include <cereal/archives/json.hpp> ++#include <iostream> ++#include <fstream> ++#include <cereal/types/vector.hpp> ++ ++#include <xyz/openbmc_project/State/Boot/PostCode/server.hpp> ++#include <xyz/openbmc_project/Common/error.hpp> ++#include <phosphor-logging/elog-errors.hpp> ++#include <xyz/openbmc_project/State/Host/server.hpp> ++ ++#define MaxPostCodeCycles 100 ++ ++const static constexpr char *PostCodePath = ++ "/xyz/openbmc_project/state/boot/raw"; ++const static constexpr char *PropertiesIntf = ++ "org.freedesktop.DBus.Properties"; ++const static constexpr char *PostCodeListPath = ++ "/var/lib/phosphor-post-code-manager/"; ++const static constexpr char *CurrentBootCycleIndexName = ++ "CurrentBootCycleIndex"; ++const static constexpr char *HostStatePath = ++ "/xyz/openbmc_project/state/host0"; ++ ++ ++struct EventDeleter ++{ ++ void operator()(sd_event *event) const ++ { ++ event = sd_event_unref(event); ++ } ++}; ++using EventPtr = std::unique_ptr<sd_event, EventDeleter>; ++namespace fs = std::experimental::filesystem; ++namespace StateServer = sdbusplus::xyz::openbmc_project::State::server; ++ ++using post_code = ++ sdbusplus::xyz::openbmc_project::State::Boot::server::PostCode; ++ ++struct PostCode : sdbusplus::server::object_t<post_code> ++{ ++ PostCode(sdbusplus::bus::bus& bus, const char* path, ++ EventPtr &event) : ++ sdbusplus::server::object_t<post_code>(bus, path), ++ bus(bus), ++ propertiesChangedSignalRaw( ++ bus, ++ sdbusplus::bus::match::rules::type::signal() + ++ sdbusplus::bus::match::rules::member("PropertiesChanged") + ++ sdbusplus::bus::match::rules::path(PostCodePath) + ++ sdbusplus::bus::match::rules::interface(PropertiesIntf), ++ [this](sdbusplus::message::message &msg) { ++ std::string objectName; ++ std::map<std::string, sdbusplus::message::variant<uint64_t>> msgData; ++ msg.read(objectName, msgData); ++ // Check if it was the Value property that changed. ++ auto valPropMap = msgData.find("Value"); ++ { ++ if (valPropMap != msgData.end()) ++ { ++ this->savePostCodes(sdbusplus::message::variant_ns::get<uint64_t>(valPropMap->second)); ++ } ++ } ++ }), ++ propertiesChangedSignalCurrentHostState( ++ bus, ++ sdbusplus::bus::match::rules::type::signal() + ++ sdbusplus::bus::match::rules::member("PropertiesChanged") + ++ sdbusplus::bus::match::rules::path(HostStatePath) + ++ sdbusplus::bus::match::rules::interface(PropertiesIntf), ++ [this](sdbusplus::message::message &msg) { ++ std::string objectName; ++ std::map<std::string, sdbusplus::message::variant<std::string>> msgData; ++ msg.read(objectName, msgData); ++ // Check if it was the Value property that changed. ++ auto valPropMap = msgData.find("CurrentHostState"); ++ { ++ if (valPropMap != msgData.end()) ++ { ++ StateServer::Host::HostState currentHostState = ++ StateServer::Host::convertHostStateFromString( ++ sdbusplus::message::variant_ns::get<std::string>(valPropMap->second)); ++ if (currentHostState == StateServer::Host::HostState::Off) ++ { ++ if (this->currentBootCycleIndex() >= this->maxBootCycleNum()) ++ { ++ this->currentBootCycleIndex(1); ++ } else{ ++ this->currentBootCycleIndex(this->currentBootCycleIndex() + 1); ++ } ++ this->postCodes.clear(); ++ } ++ } ++ } ++ }) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "PostCode is created"); ++ auto dir = fs::path(PostCodeListPath); ++ fs::create_directories(dir); ++ strPostCodeListPath = PostCodeListPath; ++ strCurrentBootCycleIndexName = CurrentBootCycleIndexName; ++ uint16_t index = 0; ++ deserialize(fs::path(strPostCodeListPath + strCurrentBootCycleIndexName), index); ++ currentBootCycleIndex(index); ++ maxBootCycleNum(MaxPostCodeCycles); ++ if (currentBootCycleIndex() >= maxBootCycleNum()) ++ { ++ currentBootCycleIndex(1); ++ } else{ ++ currentBootCycleIndex(currentBootCycleIndex() + 1); ++ } ++ } ++ ~PostCode() ++ { ++ ++ } ++ ++ std::vector<uint64_t> getPostCodes(uint16_t index) override; ++ ++ private: ++ sdbusplus::bus::bus& bus; ++ std::vector<uint64_t> postCodes; ++ std::string strPostCodeListPath; ++ std::string strCurrentBootCycleIndexName; ++ void savePostCodes(uint64_t code); ++ sdbusplus::bus::match_t propertiesChangedSignalRaw; ++ sdbusplus::bus::match_t propertiesChangedSignalCurrentHostState; ++ fs::path serialize(const std::string& path); ++ bool deserialize(const fs::path& path, uint16_t& index); ++ bool deserializePostCodes(const fs::path& path, std::vector<uint64_t> &codes); ++}; +diff --git a/service_files/xyz.openbmc_project.State.Boot.PostCode.service b/service_files/xyz.openbmc_project.State.Boot.PostCode.service +new file mode 100644 +index 0000000..67bc43f +--- /dev/null ++++ b/service_files/xyz.openbmc_project.State.Boot.PostCode.service +@@ -0,0 +1,11 @@ ++[Unit] ++Description=Post code manager ++ ++[Service] ++ExecStart=/usr/bin/env post-code-manager ++SyslogIdentifier=post-code-manager ++Type=dbus ++BusName=xyz.openbmc_project.State.Boot.PostCode ++ ++[Install] ++WantedBy=obmc-standby.target +diff --git a/src/main.cpp b/src/main.cpp +new file mode 100644 +index 0000000..4a74b29 +--- /dev/null ++++ b/src/main.cpp +@@ -0,0 +1,61 @@ ++/* ++// Copyright (c) 2019 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++#include "post_code.hpp" ++ ++int main(int argc, char* argv[]) ++{ ++ int ret = 0; ++ ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "Start post code manager service..."); ++ ++ sd_event* event = nullptr; ++ ret = sd_event_default(&event); ++ if (ret < 0) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error creating a default sd_event handler"); ++ return ret; ++ } ++ EventPtr eventP{event}; ++ event = nullptr; ++ ++ sdbusplus::bus::bus bus = sdbusplus::bus::new_default(); ++ sdbusplus::server::manager_t m{bus, DBUS_OBJECT_NAME}; ++ ++ bus.request_name(DBUS_INTF_NAME); ++ ++ PostCode postCode{bus, DBUS_OBJECT_NAME, eventP}; ++ ++ try ++ { ++ bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL); ++ ret = sd_event_loop(eventP.get()); ++ if (ret < 0) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error occurred during the sd_event_loop", ++ phosphor::logging::entry("RET=%d", ret)); ++ } ++ } ++ catch (std::exception& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ return -1; ++ } ++ return 0; ++ ++} +diff --git a/src/post_code.cpp b/src/post_code.cpp +new file mode 100644 +index 0000000..983eeee +--- /dev/null ++++ b/src/post_code.cpp +@@ -0,0 +1,109 @@ ++/* ++// Copyright (c) 2019 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++#include "post_code.hpp" ++std::vector<uint64_t> PostCode::getPostCodes(uint16_t index) ++{ ++ std::vector<uint64_t> codes; ++ ++ if (currentBootCycleIndex() == index) ++ return postCodes; ++ deserializePostCodes(fs::path(strPostCodeListPath + std::to_string(index)), codes); ++ return codes; ++} ++void PostCode::savePostCodes(uint64_t code) ++{ ++ postCodes.push_back(code); ++ serialize(fs::path(PostCodeListPath)); ++ return; ++} ++ ++fs::path PostCode::serialize(const std::string& path) ++{ ++ try ++ { ++ uint16_t index = currentBootCycleIndex(); ++ fs::path fullPath(path + strCurrentBootCycleIndexName); ++ std::ofstream os(fullPath.c_str(), std::ios::binary); ++ cereal::JSONOutputArchive oarchive(os); ++ oarchive(index); ++ ++ std::ofstream osPostCodes((path + std::to_string(currentBootCycleIndex())).c_str(), std::ios::binary); ++ cereal::JSONOutputArchive oarchivePostCodes(osPostCodes); ++ oarchivePostCodes(postCodes); ++ ++ return path; ++ } ++ catch (cereal::Exception& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ } ++ catch (const fs::filesystem_error& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ } ++} ++ ++bool PostCode::deserialize(const fs::path& path, uint16_t& index) ++{ ++ try ++ { ++ if (fs::exists(path)) ++ { ++ std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); ++ cereal::JSONInputArchive iarchive(is); ++ iarchive(index); ++ return true; ++ } ++ return false; ++ } ++ catch (cereal::Exception& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ return false; ++ } ++ catch (const fs::filesystem_error& e) ++ { ++ return false; ++ } ++ ++ return false; ++} ++ ++bool PostCode::deserializePostCodes(const fs::path& path, std::vector<uint64_t> &codes) ++{ ++ try ++ { ++ if (fs::exists(path)) ++ { ++ std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); ++ cereal::JSONInputArchive iarchive(is); ++ iarchive(codes); ++ return true; ++ } ++ return false; ++ } ++ catch (cereal::Exception& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ return false; ++ } ++ catch (const fs::filesystem_error& e) ++ { ++ return false; ++ } ++ ++ return false; ++} +-- +2.19.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/post-code-manager_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/state/post-code-manager_git.bb new file mode 100644 index 000000000..8f2ead18a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/post-code-manager_git.bb @@ -0,0 +1,34 @@ +SUMMARY = "Phosphor post code manager" +DESCRIPTION = "Post Code Manager" + +SRC_URI = "git://github.com/openbmc/phosphor-post-code-manager.git" +SRCREV = "7f50dcaa6feb66cf5307b8a0e4742a36a50eed29" + +S = "${WORKDIR}/git" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" + +inherit cmake pkgconfig systemd + +SYSTEMD_SERVICE_${PN} += "xyz.openbmc_project.State.Boot.PostCode.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI += "file://0001-Implement-post-code-manager.patch" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb new file mode 100644 index 000000000..6d614590d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb @@ -0,0 +1,19 @@ +SUMMARY = "Callback Manager" +DESCRIPTION = "D-Bus daemon that registers matches that trigger method calls" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground;protocol=ssh" + +inherit cmake systemd +DEPENDS = "boost sdbusplus" + +PV = "0.1+git${SRCPV}" +SRCREV = "f4d4bfc3296cb27feb17aa5d1d93b3061b56ce10" + +S = "${WORKDIR}/git/callback-manager" + +SYSTEMD_SERVICE_${PN} += "callback-manager.service" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENCE;md5=7becf906c8f8d03c237bad13bc3dac53" + +EXTRA_OECMAKE = "-DYOCTO=1" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch new file mode 100644 index 000000000..5c2879d8f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch @@ -0,0 +1,1648 @@ +From 726be648df955f5ae94f34391adc0e88f6956345 Mon Sep 17 00:00:00 2001 +From: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Date: Mon, 2 Jul 2018 19:23:25 -0700 +Subject: [PATCH] Added suport for multiple user manager services + +Support added for SSSD service implementation + +Signed-off-by: Alberto Salazar Perez <alberto.salazar.perez@intel.com> +Signed-off-by: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +--- + Makefile.am | 5 +- + mainapp.cpp | 89 ++++++- + user_mgr.cpp | 293 +++------------------ + user_mgr.hpp | 9 +- + user_service.cpp | 781 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + user_service.hpp | 233 +++++++++++++++++ + 6 files changed, 1141 insertions(+), 269 deletions(-) + create mode 100644 user_service.cpp + create mode 100644 user_service.hpp + +diff --git a/Makefile.am b/Makefile.am +index b138aea..288b780 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,11 +1,12 @@ + sbin_PROGRAMS = phosphor-user-manager + +-noinst_HEADERS = user_mgr.hpp users.hpp ++noinst_HEADERS = user_mgr.hpp users.hpp user_service.hpp + + phosphor_user_manager_SOURCES = \ + mainapp.cpp \ + user_mgr.cpp \ +- users.cpp ++ users.cpp \ ++ user_service.cpp + + phosphor_user_manager_LDFLAGS = $(SDBUSPLUS_LIBS) \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ +diff --git a/mainapp.cpp b/mainapp.cpp +index c9da030..03c406a 100644 +--- a/mainapp.cpp ++++ b/mainapp.cpp +@@ -14,18 +14,105 @@ + * limitations under the License. + */ + #include <string> ++#include <iostream> ++#include <getopt.h> + #include "user_mgr.hpp" ++#include "user_service.hpp" + #include "config.h" + + // D-Bus root for user manager + constexpr auto USER_MANAGER_ROOT = "/xyz/openbmc_project/user"; + ++void printUsage() ++{ ++ std::string usage = ++ R"(Usage: ++ phosphor-user-manager [OPTIONS] ++ ++Backend DBUS service for OpenBMC User Management. ++If no OPTIONS are specified, shadow file will be used. ++ ++Options: ++ -s, --service={shadow|sssd} ++ Specify the authentication service to use: ++ 'shadow' will use the /etc/shadow file. ++ 'sssd' will use the sssd service domains. ++ -h, --help Displays this help message. ++)"; ++ std::cerr << usage; ++} ++ ++void parseArgs(int argc, char** argv, ++ phosphor::user::UserService::ServiceType& srvc) ++{ ++ const std::string shortOpts{"s:h"}; ++ const struct option longOpts[] = {{"service", 1, nullptr, 's'}, ++ {"help", 0, nullptr, 'h'}, ++ {nullptr, 0, nullptr, 0}}; ++ ++ while (true) ++ { ++ const auto opt = ++ getopt_long(argc, argv, shortOpts.c_str(), longOpts, nullptr); ++ ++ if (opt == -1) ++ { ++ if (srvc == phosphor::user::UserService::ServiceType::none) ++ { ++ srvc = phosphor::user::UserService::ServiceType::shadow; ++ } ++ break; ++ } ++ ++ switch (opt) ++ { ++ case 's': ++ { ++ std::string srvcStr{optarg}; ++ if (!srvcStr.compare("shadow")) ++ { ++ srvc = phosphor::user::UserService::ServiceType::shadow; ++ } ++ else if (!srvcStr.compare("sssd")) ++ { ++ srvc = phosphor::user::UserService::ServiceType::sssd; ++ } ++ else ++ { ++ std::cerr << "Error. '" << srvcStr << "' is not a valid" ++ << " authentication service." << std::endl; ++ printUsage(); ++ exit(1); ++ } ++ } ++ break; ++ ++ case 'h': ++ { ++ printUsage(); ++ exit(0); ++ } ++ ++ default: ++ { ++ printUsage(); ++ exit(1); ++ } ++ } ++ } ++} ++ + int main(int argc, char** argv) + { ++ // Check command line options. Exit if error. ++ phosphor::user::UserService::ServiceType srvc = ++ phosphor::user::UserService::ServiceType::none; ++ parseArgs(argc, argv, srvc); ++ + auto bus = sdbusplus::bus::new_default(); + sdbusplus::server::manager::manager objManager(bus, USER_MANAGER_ROOT); + +- phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT); ++ phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT, srvc); + + // Claim the bus now + bus.request_name(USER_MANAGER_BUSNAME); +diff --git a/user_mgr.cpp b/user_mgr.cpp +index 47edf7d..32f3f4c 100644 +--- a/user_mgr.cpp ++++ b/user_mgr.cpp +@@ -14,26 +14,20 @@ + // limitations under the License. + */ + +-#include <shadow.h> +-#include <unistd.h> +-#include <sys/types.h> +-#include <sys/wait.h> ++#include <cstdio> ++ + #include <fstream> + #include <grp.h> + #include <pwd.h> + #include <regex> +-#include <algorithm> +-#include <numeric> +-#include <boost/process/child.hpp> +-#include <boost/process/io.hpp> + #include <boost/algorithm/string/split.hpp> + #include <xyz/openbmc_project/Common/error.hpp> + #include <xyz/openbmc_project/User/Common/error.hpp> + #include <phosphor-logging/log.hpp> + #include <phosphor-logging/elog.hpp> + #include <phosphor-logging/elog-errors.hpp> ++#include <stdexcept> + #include "shadowlock.hpp" +-#include "file.hpp" + #include "user_mgr.hpp" + #include "users.hpp" + #include "config.h" +@@ -43,12 +37,10 @@ namespace phosphor + namespace user + { + +-static constexpr const char *passwdFileName = "/etc/passwd"; + static constexpr size_t ipmiMaxUsers = 15; + static constexpr size_t ipmiMaxUserNameLen = 16; + static constexpr size_t systemMaxUserNameLen = 30; + static constexpr size_t maxSystemUsers = 30; +-static constexpr const char *grpSsh = "ssh"; + static constexpr uint8_t minPasswdLength = 8; + static constexpr int success = 0; + static constexpr int failure = -1; +@@ -94,79 +86,6 @@ using NoResource = + + using Argument = xyz::openbmc_project::Common::InvalidArgument; + +-template <typename... ArgTypes> +-static std::vector<std::string> executeCmd(const char *path, +- ArgTypes &&... tArgs) +-{ +- std::vector<std::string> stdOutput; +- boost::process::ipstream stdOutStream; +- boost::process::child execProg(path, const_cast<char *>(tArgs)..., +- boost::process::std_out > stdOutStream); +- std::string stdOutLine; +- +- while (stdOutStream && std::getline(stdOutStream, stdOutLine) && +- !stdOutLine.empty()) +- { +- stdOutput.emplace_back(stdOutLine); +- } +- +- execProg.wait(); +- +- int retCode = execProg.exit_code(); +- if (retCode) +- { +- log<level::ERR>("Command execution failed", entry("PATH=%d", path), +- entry("RETURN_CODE:%d", retCode)); +- elog<InternalFailure>(); +- } +- +- return stdOutput; +-} +- +-static std::string getCSVFromVector(std::vector<std::string> vec) +-{ +- switch (vec.size()) +- { +- case 0: +- { +- return ""; +- } +- break; +- +- case 1: +- { +- return std::string{vec[0]}; +- } +- break; +- +- default: +- { +- return std::accumulate( +- std::next(vec.begin()), vec.end(), vec[0], +- [](std::string a, std::string b) { return a + ',' + b; }); +- } +- } +-} +- +-static bool removeStringFromCSV(std::string &csvStr, const std::string &delStr) +-{ +- std::string::size_type delStrPos = csvStr.find(delStr); +- if (delStrPos != std::string::npos) +- { +- // need to also delete the comma char +- if (delStrPos == 0) +- { +- csvStr.erase(delStrPos, delStr.size() + 1); +- } +- else +- { +- csvStr.erase(delStrPos - 1, delStr.size() + 1); +- } +- return true; +- } +- return false; +-} +- + bool UserMgr::isUserExist(const std::string &userName) + { + if (userName.empty()) +@@ -293,39 +212,14 @@ void UserMgr::createUser(std::string userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserExists(userName); + throwForUserNameConstraints(userName, groupNames); + throwForMaxGrpUserCount(groupNames); + +- std::string groups = getCSVFromVector(groupNames); +- bool sshRequested = removeStringFromCSV(groups, grpSsh); +- +- // treat privilege as a group - This is to avoid using different file to +- // store the same. +- if (!priv.empty()) +- { +- if (groups.size() != 0) +- { +- groups += ","; +- } +- groups += priv; +- } +- try +- { +- executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), +- "-m", "-N", "-s", +- (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e", +- (enabled ? "" : "1970-01-02")); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("Unable to create new user"); +- elog<InternalFailure>(); +- } ++ // Tell the User Service to create a new user with the info provided. ++ userSrvc->createUser(userName, groupNames, priv, enabled); + +- // Add the users object before sending out the signal ++ // Add the users to the local list before sending out the signal + std::string userObj = std::string(usersObjPath) + "/" + userName; + std::sort(groupNames.begin(), groupNames.end()); + usersList.emplace( +@@ -339,19 +233,11 @@ void UserMgr::createUser(std::string userName, + + void UserMgr::deleteUser(std::string userName) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); +- try +- { +- executeCmd("/usr/sbin/userdel", userName.c_str(), "-r"); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("User delete failed", +- entry("USER_NAME=%s", userName.c_str())); +- elog<InternalFailure>(); +- } ++ ++ // Tell the User Service to delete user ++ userSrvc->deleteUser(userName); ++ // Then delete user from local list + + usersList.erase(userName); + +@@ -362,24 +248,13 @@ void UserMgr::deleteUser(std::string userName) + + void UserMgr::renameUser(std::string userName, std::string newUserName) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); + throwForUserExists(newUserName); + throwForUserNameConstraints(newUserName, + usersList[userName].get()->userGroups()); +- try +- { +- std::string newHomeDir = "/home/" + newUserName; +- executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(), +- userName.c_str(), "-d", newHomeDir.c_str(), "-m"); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("User rename failed", +- entry("USER_NAME=%s", userName.c_str())); +- elog<InternalFailure>(); +- } ++ // Call The User Service to rename user on the system ++ userSrvc->renameUser(userName, newUserName); ++ // Update local list to reflect the name change + const auto &user = usersList[userName]; + std::string priv = user.get()->userPrivilege(); + std::vector<std::string> groupNames = user.get()->userGroups(); +@@ -403,8 +278,6 @@ void UserMgr::updateGroupsAndPriv(const std::string &userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); + const std::vector<std::string> &oldGroupNames = + usersList[userName].get()->userGroups(); +@@ -420,29 +293,8 @@ void UserMgr::updateGroupsAndPriv(const std::string &userName, + throwForMaxGrpUserCount(groupNames); + } + +- std::string groups = getCSVFromVector(groupNames); +- bool sshRequested = removeStringFromCSV(groups, grpSsh); +- +- // treat privilege as a group - This is to avoid using different file to +- // store the same. +- if (!priv.empty()) +- { +- if (groups.size() != 0) +- { +- groups += ","; +- } +- groups += priv; +- } +- try +- { +- executeCmd("/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), +- "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("Unable to modify user privilege / groups"); +- elog<InternalFailure>(); +- } ++ // Call The User Service to update user groups and priv on the system ++ userSrvc->updateGroupsAndPriv(userName, groupNames, priv); + + log<level::INFO>("User groups / privilege updated successfully", + entry("USER_NAME=%s", userName.c_str())); +@@ -638,19 +490,9 @@ int UserMgr::setPamModuleArgValue(const std::string &moduleName, + + void UserMgr::userEnable(const std::string &userName, bool enabled) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); +- try +- { +- executeCmd("/usr/sbin/usermod", userName.c_str(), "-e", +- (enabled ? "" : "1970-01-02")); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("Unable to modify user enabled state"); +- elog<InternalFailure>(); +- } ++ // Call The User Service to update user groups and priv on the system ++ userSrvc->updateUserStatus(userName, enabled); + + log<level::INFO>("User enabled/disabled state updated successfully", + entry("USER_NAME=%s", userName.c_str()), +@@ -728,49 +570,8 @@ bool UserMgr::userLockedForFailedAttempt(const std::string &userName, + + UserSSHLists UserMgr::getUserAndSshGrpList() + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); +- +- std::vector<std::string> userList; +- std::vector<std::string> sshUsersList; +- struct passwd pw, *pwp = nullptr; +- std::array<char, 1024> buffer{}; +- +- phosphor::user::File passwd(passwdFileName, "r"); +- if ((passwd)() == NULL) +- { +- log<level::ERR>("Error opening the passwd file"); +- elog<InternalFailure>(); +- } +- +- while (true) +- { +- auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(), +- &pwp); +- if ((r != 0) || (pwp == NULL)) +- { +- // Any error, break the loop. +- break; +- } +- // Add all users whose UID >= 1000 and < 65534 +- // and special UID 0. +- if ((pwp->pw_uid == 0) || +- ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) +- { +- std::string userName(pwp->pw_name); +- userList.emplace_back(userName); +- +- // ssh doesn't have separate group. Check login shell entry to +- // get all users list which are member of ssh group. +- std::string loginShell(pwp->pw_shell); +- if (loginShell == "/bin/sh") +- { +- sshUsersList.emplace_back(userName); +- } +- } +- } +- endpwent(); +- return std::make_pair(std::move(userList), std::move(sshUsersList)); ++ // Call The User Service to get the User and SSUsers lists ++ return std::move(userSrvc->getUserAndSshGrpList()); + } + + size_t UserMgr::getIpmiUsersCount() +@@ -781,49 +582,14 @@ size_t UserMgr::getIpmiUsersCount() + + bool UserMgr::isUserEnabled(const std::string &userName) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); +- std::array<char, 4096> buffer{}; +- struct spwd spwd; +- struct spwd *resultPtr = nullptr; +- int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), +- buffer.max_size(), &resultPtr); +- if (!status && (&spwd == resultPtr)) +- { +- if (resultPtr->sp_expire >= 0) +- { +- return false; // user locked out +- } +- return true; +- } +- return false; // assume user is disabled for any error. ++ // Call The User Service to verify if user is enabled ++ return userSrvc->isUserEnabled(userName); + } + + std::vector<std::string> UserMgr::getUsersInGroup(const std::string &groupName) + { +- std::vector<std::string> usersInGroup; +- // Should be more than enough to get the pwd structure. +- std::array<char, 4096> buffer{}; +- struct group grp; +- struct group *resultPtr = nullptr; +- +- int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), +- buffer.max_size(), &resultPtr); +- +- if (!status && (&grp == resultPtr)) +- { +- for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) +- { +- usersInGroup.emplace_back(*(grp.gr_mem)); +- } +- } +- else +- { +- log<level::ERR>("Group not found", +- entry("GROUP=%s", groupName.c_str())); +- // Don't throw error, just return empty userList - fallback +- } +- return usersInGroup; ++ // Call The User Service to get the users that belong to a group ++ return std::move(userSrvc->getUsersInGroup(groupName)); + } + + DbusUserObj UserMgr::getPrivilegeMapperObject(void) +@@ -1018,11 +784,9 @@ void UserMgr::initUserObjects(void) + { + // All user management lock has to be based on /etc/shadow + phosphor::user::shadow::Lock lock(); +- std::vector<std::string> userNameList; +- std::vector<std::string> sshGrpUsersList; + UserSSHLists userSSHLists = getUserAndSshGrpList(); +- userNameList = std::move(userSSHLists.first); +- sshGrpUsersList = std::move(userSSHLists.second); ++ std::vector<std::string> userNameList = std::move(userSSHLists.first); ++ std::vector<std::string> sshGrpUsersList = std::move(userSSHLists.second); + + if (!userNameList.empty()) + { +@@ -1077,8 +841,10 @@ void UserMgr::initUserObjects(void) + } + } + +-UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : +- UserMgrIface(bus, path), AccountPolicyIface(bus, path), bus(bus), path(path) ++UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path, ++ UserService::ServiceType srvc) : ++ UserMgrIface(bus, path), ++ AccountPolicyIface(bus, path), bus(bus), path(path) + { + UserMgrIface::allPrivileges(privMgr); + std::sort(groupsMgr.begin(), groupsMgr.end()); +@@ -1186,6 +952,7 @@ UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : + } + AccountPolicyIface::accountUnlockTimeout(value32); + } ++ userSrvc = std::make_unique<UserService>(srvc, groupsMgr, privMgr); + initUserObjects(); + } + +diff --git a/user_mgr.hpp b/user_mgr.hpp +index c78174d..9d9c842 100644 +--- a/user_mgr.hpp ++++ b/user_mgr.hpp +@@ -21,6 +21,7 @@ + #include <unordered_map> + #include <variant> + #include "users.hpp" ++#include "user_service.hpp" + + namespace phosphor + { +@@ -28,8 +29,6 @@ namespace user + { + + using UserMgrIface = sdbusplus::xyz::openbmc_project::User::server::Manager; +-using UserSSHLists = +- std::pair<std::vector<std::string>, std::vector<std::string>>; + using AccountPolicyIface = + sdbusplus::xyz::openbmc_project::User::server::AccountPolicy; + +@@ -71,8 +70,10 @@ class UserMgr : public UserMgrIface, AccountPolicyIface + * + * @param[in] bus - sdbusplus handler + * @param[in] path - D-Bus path ++ * @param[in] srvc - User service to be used + */ +- UserMgr(sdbusplus::bus::bus &bus, const char *path); ++ UserMgr(sdbusplus::bus::bus &bus, const char *path, ++ UserService::ServiceType srvc); + + /** @brief create user method. + * This method creates a new user as requested +@@ -181,6 +182,8 @@ class UserMgr : public UserMgrIface, AccountPolicyIface + /** @brief object path */ + const std::string path; + ++ /** @brief user service to be used */ ++ std::unique_ptr<UserService> userSrvc; + /** @brief privilege manager container */ + std::vector<std::string> privMgr = {"priv-admin", "priv-operator", + "priv-user", "priv-callback"}; +diff --git a/user_service.cpp b/user_service.cpp +new file mode 100644 +index 0000000..9bb602c +--- /dev/null ++++ b/user_service.cpp +@@ -0,0 +1,781 @@ ++/* ++// Copyright (c) 2018 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++ ++#include <grp.h> ++#include <pwd.h> ++#include <numeric> ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++#include <boost/algorithm/string/split.hpp> ++#include "shadowlock.hpp" ++#include "file.hpp" ++#include "user_service.hpp" ++ ++/* anonymous namespace for User Service interface implementations. ++// Each class inside this namespace implements a special service ++// to be used for the User Manager class. This can be extended to use ++// other user management services and it should be as simple as ++// adding a new class which inherits from phosphor::user::UserServiceInterface ++*/ ++ ++namespace ++{ ++ ++std::string getCSVFromVector(std::vector<std::string> vec) ++{ ++ switch (vec.size()) ++ { ++ case 0: ++ { ++ return ""; ++ } ++ break; ++ ++ case 1: ++ { ++ return std::string{vec[0]}; ++ } ++ break; ++ ++ default: ++ { ++ return std::accumulate( ++ std::next(vec.begin()), vec.end(), vec[0], ++ [](std::string a, std::string b) { return a + ',' + b; }); ++ } ++ } ++} ++ ++bool removeStringFromCSV(std::string &csvStr, const std::string &delStr) ++{ ++ std::string::size_type delStrPos = csvStr.find(delStr); ++ if (delStrPos != std::string::npos) ++ { ++ // need to also delete the comma char ++ if (delStrPos == 0) ++ { ++ csvStr.erase(delStrPos, delStr.size() + 1); ++ } ++ else ++ { ++ csvStr.erase(delStrPos - 1, delStr.size() + 1); ++ } ++ return true; ++ } ++ return false; ++} ++ ++class ShadowService : public phosphor::user::UserServiceInterface ++{ ++ public: ++ ShadowService() = default; ++ ++ ~ShadowService() = default; ++ ++ phosphor::user::UserSSHLists getUserAndSshGrpList() const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ std::vector<std::string> userList; ++ std::vector<std::string> sshUsersList; ++ ++ struct passwd pw, *pwp = nullptr; ++ std::array<char, 1024> buffer{}; ++ ++ phosphor::user::File passwd(passwdFileName, "r"); ++ if ((passwd)() == NULL) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error opening the passwd file"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ while (true) ++ { ++ auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), ++ buffer.max_size(), &pwp); ++ if ((r != 0) || (pwp == NULL)) ++ { ++ // Any error, break the loop. ++ break; ++ } ++ // Add all users whose UID >= 1000 and < 65534 ++ // and special UID 0. ++ if ((pwp->pw_uid == 0) || ++ ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) ++ { ++ std::string userName(pwp->pw_name); ++ userList.emplace_back(userName); ++ ++ // ssh doesn't have separate group. Check login shell entry to ++ // get all users list which are member of ssh group. ++ std::string loginShell(pwp->pw_shell); ++ if (loginShell == "/bin/sh") ++ { ++ sshUsersList.emplace_back(userName); ++ } ++ } ++ } ++ endpwent(); ++ return std::make_pair(std::move(userList), std::move(sshUsersList)); ++ } ++ ++ std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const override ++ { ++ std::vector<std::string> usersInGroup; ++ // Should be more than enough to get the pwd structure. ++ std::array<char, 4096> buffer{}; ++ struct group grp; ++ struct group *grpPtr = &grp; ++ struct group *resultPtr; ++ ++ int status = getgrnam_r(groupName.c_str(), grpPtr, buffer.data(), ++ buffer.max_size(), &resultPtr); ++ ++ if (!status && (grpPtr == resultPtr)) ++ { ++ for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) ++ { ++ usersInGroup.emplace_back(*(grp.gr_mem)); ++ } ++ } ++ else ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Group not found", ++ phosphor::logging::entry("GROUP=%s", groupName.c_str())); ++ // Don't throw error, just return empty usersInGroup - fallback ++ } ++ return usersInGroup; ++ } ++ ++ void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups.append(","); ++ } ++ groups.append(priv); ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), ++ "-m", "-N", "-s", (sshRequested ? "/bin/sh" : "/bin/nologin"), ++ "-e", (enabled ? "" : "1970-01-02")); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create new user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void renameUser(const std::string &userName, ++ const std::string &newUserName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ try ++ { ++ std::string newHomeDir = "/home/" + newUserName; ++ phosphor::user::executeCmd("/usr/sbin/usermod", "-l", ++ newUserName.c_str(), userName.c_str(), ++ "-d", newHomeDir.c_str(), "-m"); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "User rename failed", ++ phosphor::logging::entry("USER_NAME=%s", userName.c_str())); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void deleteUser(const std::string &userName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/userdel", userName.c_str(), ++ "-r"); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "User delete failed", ++ phosphor::logging::entry("USER_NAME=%s", userName.c_str())); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same. ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), ++ "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to modify user privilege / groups"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateUserStatus(const std::string &userName, ++ const bool &enabled) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/usermod", userName.c_str(), ++ "-e", (enabled ? "" : "1970-01-02")); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to modify user enabled state"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ bool isUserEnabled(const std::string &userName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ std::array<char, 4096> buffer{}; ++ struct spwd spwd; ++ struct spwd *resultPtr = nullptr; ++ int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), ++ buffer.max_size(), &resultPtr); ++ if (!status && (&spwd == resultPtr)) ++ { ++ if (resultPtr->sp_expire >= 0) ++ { ++ return false; // user locked out ++ } ++ return true; ++ } ++ return false; // assume user is disabled for any error. ++ } ++ ++ std::vector<std::string> ++ getUserGroups(const std::string &userName) const override ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "ShadowService::getUserGroups not implemented!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ return std::vector<std::string>(); ++ } ++ ++ void createGroup(const std::string &groupName) const override ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "ShadowService::createGroup not implemented!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ private: ++ static constexpr const char *passwdFileName = "/etc/passwd"; ++}; ++ ++class SSSDService : public phosphor::user::UserServiceInterface ++{ ++ public: ++ SSSDService(const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++ { ++ ++ createGroup(lockedGrp); ++ for (const auto &g : groups) ++ { ++ createGroup(g); ++ } ++ for (const auto &p : privs) ++ { ++ createGroup(p); ++ } ++ } ++ ++ ~SSSDService() = default; ++ ++ phosphor::user::UserSSHLists getUserAndSshGrpList() const override ++ { ++ std::vector<std::string> users; ++ std::vector<std::string> sshGroup; ++ std::vector<std::string> exeOutput; ++ ++ try ++ { ++ exeOutput = phosphor::user::executeCmd("/usr/bin/getent", "-s", ++ "sss", "passwd"); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get users information " ++ "from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ for (const auto &userLine : exeOutput) ++ { ++ std::vector<std::string> userInfo; ++ boost::algorithm::split(userInfo, userLine, ++ boost::algorithm::is_any_of(":")); ++ // At this point userInfo is a vector containing the passwd ++ // info for the user, so we know the correct positions: ++ // 0: User name. ++ // 1: Encrypted password. ++ // 2: User ID number (UID) ++ // 3: User's group ID number (GID) ++ // 4: Full name of the user (GECOS) ++ // 5: User home directory. ++ // 6: Login shell. ++ users.emplace_back(userInfo[0]); ++ ++ // ssh doesn't have separate group. Check login shell entry to ++ // get all users list which are member of ssh group. ++ if (userInfo[6] == "/bin/sh") ++ { ++ sshGroup.emplace_back(userInfo[0]); ++ } ++ } ++ ++ return std::make_pair(std::move(users), std::move(sshGroup)); ++ } ++ ++ std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const override ++ { ++ std::vector<std::string> userList; ++ std::vector<std::string> exeOutput; ++ ++ try ++ { ++ exeOutput = phosphor::user::executeCmd("/usr/sbin/sss_groupshow", ++ groupName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get group users from sssd service"); ++ // Don't throw error, just return empty usersInGroup - return ++ return userList; ++ } ++ // exeOutput should have 5 entries ++ // 0: Group ++ // 1: GID number ++ // 2: Member users ++ // 3: Is a member of ++ // 4: Member groups ++ exeOutput[2].erase( ++ exeOutput[2].begin(), ++ std::find(exeOutput[2].begin(), exeOutput[2].end(), ':')); ++ boost::algorithm::trim_left(exeOutput[2]); ++ boost::algorithm::split(userList, exeOutput[2], ++ boost::algorithm::is_any_of(",")); ++ return userList; ++ } ++ ++ void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const override ++ { ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/sss_useradd", "-m", "-G", groups.c_str(), "-s", ++ (sshRequested ? "/bin/sh" : "/bin/nologin"), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create new user in sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ // Sometimes the SSSD service needs some time to actually ++ // reflect the changes to the local DB to the NSS service, ++ // that is why we have this sleep here ... ++ std::this_thread::sleep_for(std::chrono::seconds(1)); ++ // update user status (locked/unlocked) ++ updateUserStatus(userName, enabled); ++ } ++ ++ void renameUser(const std::string &userName, ++ const std::string &newUserName) const override ++ { ++ std::vector<std::string> exeOutput; ++ // Local Domain for sssd doesn't have a rename feature ++ // so we need to first create a new user and then delete ++ // the old one. ++ // The only issue with this is that the password for the ++ // user will have to be reseted since it is a new user being created. ++ ++ // Get original user groups ++ std::vector<std::string> groups = getUserGroups(userName); ++ // Check if it has a "ssh" group by looking for the shell login ++ try ++ { ++ exeOutput = phosphor::user::executeCmd( ++ "/usr/bin/getent", "-s", "sss", "passwd", userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get information for user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ if (exeOutput[0].find("/bin/sh")) ++ { ++ groups.emplace_back(phosphor::user::grpSsh); ++ } ++ // Call create user with the new user names and previous groups ++ // Priv is already part of the groups so that can be empty. ++ createUser(newUserName, groups, "", isUserEnabled(userName)); ++ ++ // Now delete original user ++ deleteUser(userName); ++ } ++ ++ void deleteUser(const std::string &userName) const override ++ { ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_userdel", "-r", ++ userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to delete user from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const override ++ { ++ // local domain sssd do not allow to update all list of groups, ++ // so we will remove all groups first (except for the user one) ++ // and then all all the ones that were passed ++ std::string oldGroups = getCSVFromVector(getUserGroups(userName)); ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/sss_usermod", "-r", oldGroups.c_str(), "-a", ++ groups.c_str(), "-s", ++ (sshRequested ? "/bin/sh" : "/bin/nologin"), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to update user groups and " ++ "priv from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateUserStatus(const std::string &userName, ++ const bool &enabled) const override ++ { ++ std::string enabledStr; ++ std::string lockedStr; ++ if (isUserEnabled(userName) == enabled) ++ { ++ return; ++ } ++ if (enabled) ++ { ++ enabledStr = "-r"; ++ lockedStr = "-U"; ++ } ++ else ++ { ++ enabledStr = "-a"; ++ lockedStr = "-L"; ++ } ++ try ++ { ++ // We will add a special locked group to identify the users ++ // that have been locked out of the system. ++ // TODO: sss_usermod is not locking user accounts for the ++ // LOCAL domain, need to find the correct PAM configuration ++ // to actually lockout users for SSSD. ++ // As a workaround we are using the pam module pam_listfile.so ++ // to lockout all users that belong to the locked group. ++ phosphor::user::executeCmd("/usr/sbin/sss_usermod", ++ enabledStr.c_str(), lockedGrp.c_str(), ++ lockedStr.c_str(), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to update user status from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ bool isUserEnabled(const std::string &userName) const override ++ { ++ std::vector<std::string> userGrps = getUserGroups(userName); ++ return std::find(userGrps.begin(), userGrps.end(), lockedGrp) == ++ userGrps.end(); ++ } ++ ++ std::vector<std::string> ++ getUserGroups(const std::string &userName) const override ++ { ++ std::vector<std::string> exeOutput; ++ try ++ { ++ exeOutput = ++ phosphor::user::executeCmd("/usr/bin/groups", userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get groups for user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ std::vector<std::string> groups; ++ boost::algorithm::split(groups, exeOutput[0], ++ boost::algorithm::is_any_of(" ")); ++ // Delete group that equals user name if it exists ++ auto userNameGroup = std::find(groups.begin(), groups.end(), userName); ++ if (userNameGroup != groups.end()) ++ { ++ groups.erase(userNameGroup); ++ } ++ return groups; ++ } ++ ++ void createGroup(const std::string &groupName) const override ++ { ++ try ++ { ++ if (!groupExists(groupName)) ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_groupadd", ++ groupName.c_str()); ++ } ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create group"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ private: ++ static const std::string lockedGrp; ++ ++ bool groupExists(const std::string &groupName) const ++ { ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_groupshow", ++ groupName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ return false; ++ } ++ return true; ++ } ++}; ++ ++const std::string SSSDService::lockedGrp = "sssd_locked"; ++} // anonymous namespace ++ ++namespace phosphor ++{ ++namespace user ++{ ++ ++UserService::UserService(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++{ ++ setServiceImpl(srvcType, groups, privs); ++} ++ ++void UserService::updateServiceType(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++{ ++ usrSrvcImpl.reset(); ++ setServiceImpl(srvcType, groups, privs); ++} ++ ++void UserService::setServiceImpl(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++{ ++ switch (srvcType) ++ { ++ case ServiceType::shadow: ++ { ++ usrSrvcImpl = std::make_unique<ShadowService>(); ++ } ++ break; ++ ++ case ServiceType::sssd: ++ { ++ usrSrvcImpl = std::make_unique<SSSDService>(groups, privs); ++ } ++ break; ++ ++ case ServiceType::none: ++ default: ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Invalid service type initialization!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ break; ++ } ++} ++ ++UserService::~UserService() ++{ ++} ++ ++phosphor::user::UserSSHLists UserService::getUserAndSshGrpList() const ++{ ++ return usrSrvcImpl->getUserAndSshGrpList(); ++} ++ ++std::vector<std::string> ++ UserService::getUsersInGroup(const std::string &groupName) const ++{ ++ return usrSrvcImpl->getUsersInGroup(groupName); ++} ++ ++void UserService::createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const ++{ ++ usrSrvcImpl->createUser(userName, groupNames, priv, enabled); ++} ++ ++void UserService::renameUser(const std::string &userName, ++ const std::string &newUserName) const ++{ ++ usrSrvcImpl->renameUser(userName, newUserName); ++} ++ ++void UserService::deleteUser(const std::string &userName) const ++{ ++ usrSrvcImpl->deleteUser(userName); ++} ++ ++void UserService::updateGroupsAndPriv( ++ const std::string &userName, const std::vector<std::string> &groupNames, ++ const std::string &priv) const ++{ ++ usrSrvcImpl->updateGroupsAndPriv(userName, groupNames, priv); ++} ++ ++void UserService::updateUserStatus(const std::string &userName, ++ const bool &enabled) const ++{ ++ usrSrvcImpl->updateUserStatus(userName, enabled); ++} ++ ++bool UserService::isUserEnabled(const std::string &userName) const ++{ ++ return usrSrvcImpl->isUserEnabled(userName); ++} ++ ++std::vector<std::string> ++ UserService::getUserGroups(const std::string &userName) const ++{ ++ return usrSrvcImpl->getUserGroups(userName); ++} ++ ++} // namespace user ++} // namespace phosphor +diff --git a/user_service.hpp b/user_service.hpp +new file mode 100644 +index 0000000..97a049b +--- /dev/null ++++ b/user_service.hpp +@@ -0,0 +1,233 @@ ++/* ++// Copyright (c) 2018 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++ ++#pragma once ++#include <xyz/openbmc_project/Common/error.hpp> ++#include <xyz/openbmc_project/User/Common/error.hpp> ++#include <phosphor-logging/log.hpp> ++#include <phosphor-logging/elog.hpp> ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++ ++namespace phosphor ++{ ++namespace user ++{ ++ ++using UserSSHLists = ++ std::pair<std::vector<std::string>, std::vector<std::string>>; ++using InternalFailure = ++ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; ++using InsufficientPermission = ++ sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; ++ ++const std::string grpSsh = "ssh"; ++ ++template <typename... ArgTypes> ++std::vector<std::string> executeCmd(const char *path, ArgTypes &&... tArgs) ++{ ++ std::vector<std::string> stdOutput; ++ boost::process::ipstream stdOutStream; ++ boost::process::child execProg(path, const_cast<char *>(tArgs)..., ++ boost::process::std_out > stdOutStream); ++ std::string stdOutLine; ++ ++ while (stdOutStream && std::getline(stdOutStream, stdOutLine) && ++ !stdOutLine.empty()) ++ { ++ stdOutput.emplace_back(stdOutLine); ++ } ++ ++ execProg.wait(); ++ ++ int retCode = execProg.exit_code(); ++ if (retCode) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Command execution failed", ++ phosphor::logging::entry("PATH=%d", path), ++ phosphor::logging::entry("RETURN_CODE:%d", retCode)); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ return stdOutput; ++} ++ ++/** @class UserServiceInterface ++ * @brief Interface class for methods provided by the implemmentations ++ * of the user service. Provides the same methods as the UserService ++ * class. ++ */ ++class UserServiceInterface ++{ ++ public: ++ UserServiceInterface() = default; ++ virtual ~UserServiceInterface() = default; ++ virtual UserSSHLists getUserAndSshGrpList() const = 0; ++ virtual std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const = 0; ++ virtual void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, ++ const bool &enabled) const = 0; ++ virtual void renameUser(const std::string &userName, ++ const std::string &newUserName) const = 0; ++ virtual void deleteUser(const std::string &userName) const = 0; ++ virtual void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const = 0; ++ virtual void updateUserStatus(const std::string &userName, ++ const bool &enabled) const = 0; ++ virtual bool isUserEnabled(const std::string &userName) const = 0; ++ virtual std::vector<std::string> ++ getUserGroups(const std::string &userName) const = 0; ++ virtual void createGroup(const std::string &groupName) const = 0; ++}; ++ ++/** @class UserService ++ * @brief Responsible for managing the user service for the user manager. ++ * This service is the one responsible to actually change the user information ++ * of the application. It can support sevaral services, currently the ones ++ * supported are: ++ * ++ * 1) Shadow: Which uses the /etc/shadow file for updating the users ++ * 2) SSSD: Which uses the sssd service for a LOCAL domain only right now. ++ */ ++class UserService ++{ ++ public: ++ UserService() = delete; ++ UserService(const UserService &) = delete; ++ UserService &operator=(const UserService &) = delete; ++ UserService(UserService &&) = delete; ++ UserService &operator=(UserService &&) = delete; ++ ++ // Service Types implemented. None is used to validate. ++ enum class ServiceType ++ { ++ none, ++ shadow, ++ sssd ++ }; ++ ++ UserService(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs); ++ ~UserService(); ++ ++ /** @brief update the current Service type of the instance. ++ * This function is used to update in real time the service ++ * being used for the user management without restarting the ++ * whole service. ++ * ++ * @param[in] srvcType ++ * @param[in] groups ++ * @param[in] privs ++ */ ++ void updateServiceType(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs); ++ ++ /** @brief get user list and SSH group members list ++ * This method gets the list of users from the service. ++ * If the userlist reference is empty, all the users will be added ++ * and DBus notified about them. If the list is not empty, the function ++ * will only update list adding the missing ones to it. It will not remove ++ * any extra users on the list that are not part of the service! ++ * ++ */ ++ UserSSHLists getUserAndSshGrpList() const; ++ ++ /** @brief Get users in group. ++ * This method creates a new user as requested ++ * ++ * @param[in] groupName - Name of the group which has to be queried ++ */ ++ std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const; ++ ++ /** @brief create user method. ++ * This method creates a new user as requested ++ * ++ * @param[in] userName - Name of the user which has to be created ++ * @param[in] groupNames - Group names list, to which user has to be added. ++ * @param[in] priv - Privilege of the user. ++ * @param[in] enabled - State of the user enabled / disabled. ++ */ ++ void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const; ++ ++ /** @brief rename user method. ++ * This method renames the user as requested ++ * ++ * @param[in] userName - current name of the user ++ * @param[in] userName - user name to which it has to be renamed. ++ */ ++ void renameUser(const std::string &userName, ++ const std::string &newUserName) const; ++ ++ /** @brief delete user method. ++ * This method deletes the user as requested ++ * ++ * @param[in] userName - Name of the user which has to be deleted ++ */ ++ void deleteUser(const std::string &userName) const; ++ ++ /** @brief Updates user Groups and Privilege. ++ * ++ * @param[in] userName - Name of the user which has to be modified ++ * @param[in] groupNames - Group names list for user. ++ * @param[in] priv - Privilege of the user. ++ */ ++ void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const; ++ ++ /** @brief Updates user status ++ * If enabled = false: User will be disabled ++ * If enabled = true : User will be enabled ++ * ++ * @param[in] userName - Name of the user ++ * @param[in] enabled - Status of the user: enabled / disabled? ++ */ ++ void updateUserStatus(const std::string &userName, ++ const bool &enabled) const; ++ ++ /** @brief Verify if user is enabled or not ++ * If enabled returns true ++ * If not enabled returns false ++ * ++ * @param[in] userName - Name of the user ++ */ ++ bool isUserEnabled(const std::string &userName) const; ++ ++ /** @brief Get the list of groups a user belongs to ++ * ++ * @param[in] userName - Name of the user ++ */ ++ std::vector<std::string> getUserGroups(const std::string &userName) const; ++ ++ private: ++ // User service implementation. ++ void setServiceImpl(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs); ++ std::unique_ptr<UserServiceInterface> usrSrvcImpl; ++}; ++ ++} // namespace user ++} // namespace phosphor +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend new file mode 100644 index 000000000..1939e5ad2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +#SRC_URI = "git://github.com/openbmc/phosphor-user-manager" +SRCREV = "736648e25eb250d1e200cea961fe75bf791f1355" + + +SRC_URI += " \ + file://0005-Added-suport-for-multiple-user-manager-services.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb new file mode 100644 index 000000000..950f4932d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb @@ -0,0 +1,35 @@ + +SUMMARY = "FRB2 timer service" +DESCRIPTION = "The FRB2 timer service will monitor the mailbox register 0\ +and start a watchdog for FRB2 if the data is 1(BIOS will write this value)" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://frb2-watchdog.cpp \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${PHOSPHORBASE}/LICENSE;md5=19407077e42b1ba3d653da313f1f5b4e" + +S = "${WORKDIR}" + +inherit cmake +inherit pkgconfig pythonnative + +DEPENDS += " \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-logging \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + boost \ + " + +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + phosphor-dbus-interfaces \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format new file mode 100644 index 000000000..dd2770837 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format @@ -0,0 +1,98 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt new file mode 100644 index 000000000..bd5567d31 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required (VERSION 3.5 FATAL_ERROR) +project (frb2-watchdog CXX) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +# boost support +find_package (Boost REQUIRED) +# pkg_check_modules(Boost boost REQUIRED) +include_directories (${Boost_INCLUDE_DIRS}) +add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY) +add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED) +add_definitions (-DBOOST_ALL_NO_LIB) +add_definitions (-DBOOST_NO_RTTI) +add_definitions (-DBOOST_NO_TYPEID) +add_definitions (-DBOOST_ASIO_DISABLE_THREADS) + +# import libsystemd +find_package (PkgConfig REQUIRED) +pkg_check_modules (SYSTEMD libsystemd REQUIRED) +include_directories (${SYSTEMD_INCLUDE_DIRS}) +link_directories (${SYSTEMD_LIBRARY_DIRS}) + +# import sdbusplus +find_package (PkgConfig REQUIRED) +pkg_check_modules (SDBUSPLUSPLUS sdbusplus REQUIRED) +include_directories (${SDBUSPLUSPLUS_INCLUDE_DIRS}) +link_directories (${SDBUSPLUSPLUS_LIBRARY_DIRS}) + +# import phosphor-logging +find_package (PkgConfig REQUIRED) +pkg_check_modules (LOGGING phosphor-logging REQUIRED) +include_directories (${LOGGING_INCLUDE_DIRS}) +link_directories (${LOGGING_LIBRARY_DIRS}) + +# import phosphor-dbus-interfaces +find_package (PkgConfig REQUIRED) +pkg_check_modules (DBUSINTERFACE phosphor-dbus-interfaces REQUIRED) +include_directories (${DBUSINTERFACE_INCLUDE_DIRS}) +link_directories (${DBUSINTERFACE_LIBRARY_DIRS}) + +add_executable (frb2-watchdog frb2-watchdog.cpp) + +target_link_libraries (${PROJECT_NAME} systemd) +target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES} + phosphor_logging) +install (TARGETS frb2-watchdog DESTINATION bin) diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json new file mode 100644 index 000000000..583c255a3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json @@ -0,0 +1,12 @@ +{ + "enum_char": ".", + "line_ending": "unix", + "bullet_char": "*", + "max_subargs_per_line": 99, + "command_case": "lower", + "tab_size": 4, + "line_width": 80, + "separate_fn_name_with_space": true, + "dangle_parens": true, + "separate_ctrl_name_with_space": true +}
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp new file mode 100644 index 000000000..5356e95db --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp @@ -0,0 +1,258 @@ +/* Copyright 2018 Intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <boost/container/flat_set.hpp> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <memory> +#include <optional> +#include <phosphor-logging/log.hpp> +#include <sdbusplus/asio/object_server.hpp> +#include <sdbusplus/bus.hpp> +#include <sdbusplus/bus/match.hpp> +#include <sdbusplus/message.hpp> +#include <sdbusplus/timer.hpp> +#include <vector> +#include <xyz/openbmc_project/State/Watchdog/server.hpp> + +void handleResponse(const boost::system::error_code &err, + std::size_t bytes_transferred); + +static int mailboxDevFd = -1; + +static boost::asio::io_service io; +static auto conn = std::make_shared<sdbusplus::asio::connection>(io); +boost::asio::ip::tcp::socket mailBoxDevSocket(io); +boost::asio::deadline_timer pollTimer(io); +boost::asio::posix::stream_descriptor inputDevice(io); + +// mailbox registre data[0:0] for FRB2 enable bit +boost::asio::streambuf readBuf(1); +std::string dataRead; + +// FRB2 watchdog timeout is 6 minutes +static constexpr unsigned int frb2TimerIntervalMs = 360 * 1000; + +// mailbox device polling time interval is 2 seconds +static constexpr unsigned int pollMs = 2000; + +static constexpr unsigned int frb2Started = 1; +static constexpr unsigned int frb2Stopped = 0; + +// FRB2 status +static uint8_t frb2Status = frb2Stopped; + +static constexpr const char *mailboxDevName = "/dev/aspeed-mbox"; + +static constexpr const char frb2Bus[] = "xyz.openbmc_project.FRB2"; +static constexpr const char frb2Obj[] = "/xyz/openbmc_project/FRB2"; +static constexpr const char frb2Intf[] = "xyz.openbmc_project.FRB2"; + +static constexpr char powerBus[] = "xyz.openbmc_project.Chassis.Control.Power"; +static constexpr char powerPath[] = + "/xyz/openbmc_project/Chassis/Control/Power0"; +static constexpr char powerIntf[] = "xyz.openbmc_project.Chassis.Control.Power"; + +static constexpr char wdBus[] = "xyz.openbmc_project.Watchdog"; +static constexpr char wdPath[] = "/xyz/openbmc_project/watchdog/host0"; +static constexpr char wdIntf[] = "xyz.openbmc_project.State.Watchdog"; +static constexpr char propIntf[] = "org.freedesktop.DBus.Properties"; + +typedef boost::asio::buffers_iterator<boost::asio::const_buffers_1> iterator; + +// check if FRB2 bit is 0x1 +std::pair<iterator, bool> matchFRB2(iterator begin, iterator end) +{ + unsigned char ch = 0; + iterator i = begin; + + while (i != end) + { + ch = static_cast<unsigned char>(*i); + if (ch & 0x1) + { + return std::make_pair(i, true); + } + i++; + } + + return std::make_pair(i, false); +} + +static void startRead() +{ + boost::asio::async_read_until(inputDevice, readBuf, matchFRB2, + [&](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + handleResponse(ec, bytes_transferred); + }); +} + +template <typename T> void setProperty(const std::string &key, const T &val) +{ + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "setProperty", phosphor::logging::entry("KEY=%s", key.c_str())); + + try + { + conn->async_method_call( + [](const boost::system::error_code &err) { + if (err) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "async_method_call error!", + phosphor::logging::entry( + "ERROR=%s", + boost::system::system_error(err).what())); + } + }, + wdBus, wdPath, propIntf, "Set", wdIntf, key, + sdbusplus::message::variant_ns::variant<T>(val)); + } + catch (sdbusplus::exception::SdBusError &e) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Dbus error!", phosphor::logging::entry("ERROR=%s", e.what())); + } +} +void handleResponse(const boost::system::error_code &err, + std::size_t bytes_transferred) +{ + std::istream responseStream(&readBuf); + std::string response; + int n = 0; + uint64_t interval = frb2TimerIntervalMs; + + std::getline(responseStream, response); + responseStream.clear(); + + if (err == boost::system::errc::bad_file_descriptor) + { + + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "bad file descriptor"); + return; // we're being destroyed + } + + if (!err) + { + // FRB2 is set by BIOS + if (frb2Stopped == frb2Status) + { + // start FRB2 watchdog + frb2Status = frb2Started; + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "FRB2 enable, start FRB2 watchdog"); + setProperty( + "ExpireAction", + std::string( + "xyz.openbmc_project.State.Watchdog.Action.HardReset")); + setProperty("Interval", interval); + setProperty("TimeRemaining", interval); + setProperty("Initialized", true); + setProperty("Enabled", true); + } + } + else if (err == boost::asio::error::misc_errors::not_found) + { + // FRB2 is clear, stop FRB2 watchdog if it is started + if (frb2Started == frb2Status) + { + frb2Status = frb2Stopped; + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "FRB2 is unset, stop FRB2 watchdog"); + setProperty("Enabled", false); + } + } + else + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "handleResponse error!", + phosphor::logging::entry("ERROR=%s", + boost::system::system_error(err).what())); + } + + pollTimer.expires_from_now(boost::posix_time::milliseconds(pollMs)); + pollTimer.async_wait( + [](const boost::system::error_code &ec) { startRead(); }); +} + +int main(int argc, char **argv) +{ + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "Monitor FRB2 signal"); + + sdbusplus::bus::match_t biosPostSignal( + static_cast<sdbusplus::bus::bus &>(*conn), + sdbusplus::bus::match::rules::type::signal() + + sdbusplus::bus::match::rules::member("PostCompleted") + + sdbusplus::bus::match::rules::path(powerPath) + + sdbusplus::bus::match::rules::interface(powerIntf), + [](sdbusplus::message::message &msg) { + uint8_t value = 0; + ssize_t rc = 0; + phosphor::logging::log<phosphor::logging::level::INFO>( + "BIOS post completed signal"); + // stop FRB2 and clean mailbox + value = 0; + rc = ::pwrite(mailboxDevFd, &value, 1, 0); + if (rc != 1) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "mailbox write error!"); + } + setProperty("Enabled", false); + frb2Status = frb2Stopped; + return; + }); + + conn->request_name(frb2Bus); + + auto server = sdbusplus::asio::object_server(conn); + + std::shared_ptr<sdbusplus::asio::dbus_interface> frb2Iface = + server.add_interface(frb2Obj, frb2Intf); + + frb2Iface->register_property("frb2Status", frb2Status); + + frb2Iface->initialize(); + + mailboxDevFd = ::open(mailboxDevName, O_RDWR | O_CLOEXEC); + if (mailboxDevFd < 0) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "mailbox device open fail!"); + return -1; + } + + inputDevice.assign(mailboxDevFd); + + startRead(); + + io.run(); + + ::close(mailboxDevFd); + + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Add-expiredTimerUse-property-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Add-expiredTimerUse-property-support.patch new file mode 100644 index 000000000..54e459245 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Add-expiredTimerUse-property-support.patch @@ -0,0 +1,28 @@ +From efb3c226fc80243729901f675568b2b41142b1f9 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 18 Mar 2019 22:54:41 +0800 +Subject: [PATCH] Add expiredTimerUse property support + +When the timer expired, set expiredTimerUse property as the timer use + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + watchdog.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/watchdog.cpp b/watchdog.cpp +index aed0443..d529746 100644 +--- a/watchdog.cpp ++++ b/watchdog.cpp +@@ -101,6 +101,8 @@ void Watchdog::timeOutHandler() + action = fallback->action; + } + ++ WatchdogInherits::expiredTimerUse(WatchdogInherits::currentTimerUse()); ++ + auto target = actionTargetMap.find(action); + if (target == actionTargetMap.end()) + { +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/obmc-enable-host-watchdog@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/obmc-enable-host-watchdog@.service new file mode 100644 index 000000000..87a662f7c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/obmc-enable-host-watchdog@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Start FRB2 Watchdog%i +Wants=obmc-host-started@%i.target +After=obmc-host-started@%i.target +Wants=mapper-wait@-xyz-openbmc_project-watchdog-host%i.service +After=mapper-wait@-xyz-openbmc_project-watchdog-host%i.service +Conflicts=obmc-host-stop@%i.target +ConditionPathExists=!/run/openbmc/host@%i-on + +[Service] +Restart=always +ExecStart=/usr/bin/env frb2-watchdog +ExecStopPost=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/watchdog/host%i` /xyz/openbmc_project/watchdog/host%i org.freedesktop.DBus.Properties Set ssv xyz.openbmc_project.State.Watchdog Enabled b false" +SyslogIdentifier=obmc-enable-host-watchdog diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog@.service new file mode 100644 index 000000000..251c2257b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog@.service @@ -0,0 +1,12 @@ +[Unit] +Description=Phosphor %I watchdog + +[Service] +Restart=no +EnvironmentFile={envfiledir}/obmc/watchdog/%I +ExecStart=/usr/bin/env phosphor-watchdog --continue --service=${{SERVICE}} --path=${{DEVPATH}} \ + --action_target=xyz.openbmc_project.State.Watchdog.Action.HardReset=obmc-host-warm-reset@0.target \ + --action_target=xyz.openbmc_project.State.Watchdog.Action.PowerOff=obmc-host-shutdown@0.target \ + --action_target=xyz.openbmc_project.State.Watchdog.Action.PowerCycle=obmc-host-reboot@0.target + +SyslogIdentifier=phosphor-watchdog diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend new file mode 100644 index 000000000..5f8d53976 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend @@ -0,0 +1,6 @@ +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +SRC_URI += "file://0001-Add-expiredTimerUse-property-support.patch" + +# Remove the override to keep service running after DC cycle +SYSTEMD_OVERRIDE_${PN}_remove = "poweron.conf:phosphor-watchdog@poweron.service.d/poweron.conf" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb new file mode 100644 index 000000000..5da053f1d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb @@ -0,0 +1,13 @@ +SUMMARY = "System watchdog" +DESCRIPTION = "BMC hardware watchdog service that is used to reset BMC \ + when unrecoverable events occurs" + +inherit allarch +inherit obmc-phosphor-systemd + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SYSTEMD_SERVICE_${PN} += "system-watchdog.service" +SYSTEMD_ENVIRONMENT_FILE_${PN} += "obmc/system-watchdog/system-watchdog.conf" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf new file mode 100644 index 000000000..defe830a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf @@ -0,0 +1,3 @@ +TIMEOUT=60 +INTERVAL=10 +DEVICE=/dev/watchdog1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service new file mode 100644 index 000000000..1564fda20 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service @@ -0,0 +1,11 @@ +[Unit] +Description=BMC Hardware Watchdog Daemon + +[Service] +EnvironmentFile=/etc/default/obmc/system-watchdog/system-watchdog.conf +ExecStart=/sbin/watchdog -T ${{TIMEOUT}} -t ${{INTERVAL}} -F ${{DEVICE}} +KillSignal=SIGKILL + +[Install] +WantedBy=basic.target + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0001-Implement-KVM-in-webui.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0001-Implement-KVM-in-webui.patch new file mode 100644 index 000000000..85b0f1009 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0001-Implement-KVM-in-webui.patch @@ -0,0 +1,238 @@ +From 4c63b4e16fbc3b536a437b2ab5d5df5e846e6b83 Mon Sep 17 00:00:00 2001 +From: Ed tanous <ed@tanous.net> +Date: Sun, 22 Apr 2018 10:53:28 -0700 +Subject: [PATCH] Implement KVM in webui + +This patchset adds the infrastructure to allow KVM sessions +through the webui. A websocket capable VNC/RFB connection +on the BMC is needed for KVM sessions. + +To access, navigate to Server control -> KVM. + +Tested: Ran obmc-ikvm on the BMC, added a KVM Handler to + Phosphor Rest Server, and was able to establish a + KVM session in the webui on a Witherspoon. +Change-Id: I7dda5bec41d270ae8d0913697714d4df4ec3a257 +Signed-off-by: Ed Tanous <ed.tanous@intel.com> +Signed-off-by: Gunnar Mills <gmills@us.ibm.com> +--- + app/common/directives/app-navigation.html | 12 +++-- + app/index.js | 1 + + app/server-control/controllers/kvm-controller.html | 5 ++ + app/server-control/controllers/kvm-controller.js | 55 ++++++++++++++++++++++ + app/server-control/index.js | 5 ++ + app/server-control/styles/index.scss | 1 + + app/server-control/styles/kvm.scss | 11 +++++ + package-lock.json | 5 ++ + package.json | 1 + + webpack.config.js | 6 ++- + 10 files changed, 96 insertions(+), 6 deletions(-) + create mode 100644 app/server-control/controllers/kvm-controller.html + create mode 100644 app/server-control/controllers/kvm-controller.js + create mode 100644 app/server-control/styles/kvm.scss + +diff --git a/app/common/directives/app-navigation.html b/app/common/directives/app-navigation.html +index a45a24bcbaa1..e54b23631b3e 100644 +--- a/app/common/directives/app-navigation.html ++++ b/app/common/directives/app-navigation.html +@@ -87,19 +87,21 @@ + <a href="#/server-control/bmc-reboot" tabindex="13" ng-click="closeSubnav()">Reboot BMC</a></li> + <li ng-class="{'active': (path == '/server-control/remote-console')}"> + <a href="#/server-control/remote-console" tabindex="14" ng-click="closeSubnav()">Serial over LAN console</a></li> ++ <li ng-class="{'active': (path == '/server-control/kvm')}"> ++ <a href="#/server-control/kvm" tabindex="15" ng-click="closeSubnav()">KVM</a></li> + </ul> + <ul class="nav__second-level btn-firmware" ng-style="navStyle" ng-class="{opened: (showSubMenu && firstLevel == 'configuration')}"> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/network')}"> +- <a href="#/configuration/network" tabindex="15" ng-click="closeSubnav()">Network settings</a></li> ++ <a href="#/configuration/network" tabindex="16" ng-click="closeSubnav()">Network settings</a></li> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/snmp')}"> +- <a href="#/configuration/snmp" tabindex="16" ng-click="closeSubnav()">SNMP settings</a></li> ++ <a href="#/configuration/snmp" tabindex="17" ng-click="closeSubnav()">SNMP settings</a></li> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/firmware')}"> +- <a href="#/configuration/firmware" tabindex="17" ng-click="closeSubnav()">Firmware</a></li> ++ <a href="#/configuration/firmware" tabindex="18" ng-click="closeSubnav()">Firmware</a></li> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/date-time')}"> +- <a href="#/configuration/date-time" tabindex="18" ng-click="closeSubnav()">Date and time settings</a></li> ++ <a href="#/configuration/date-time" tabindex="19" ng-click="closeSubnav()">Date and time settings</a></li> + </ul> + <ul class="nav__second-level btn-users" ng-style="navStyle" ng-class="{opened: (showSubMenu && firstLevel == 'users')}"> + <li ng-class="{'active': (path == '/users' || path == '/users/manage-accounts')}"> +- <a href="#/users/manage-accounts" tabindex="19" ng-click="closeSubnav()">Manage user account</a></li> ++ <a href="#/users/manage-accounts" tabindex="20" ng-click="closeSubnav()">Manage user account</a></li> + </ul> + </nav> +diff --git a/app/index.js b/app/index.js +index 38df0e9896f4..a0dde4df96b7 100644 +--- a/app/index.js ++++ b/app/index.js +@@ -69,6 +69,7 @@ import power_operations_controller from './server-control/controllers/power-oper + import power_usage_controller from './server-control/controllers/power-usage-controller.js'; + import remote_console_window_controller from './server-control/controllers/remote-console-window-controller.js'; + import server_led_controller from './server-control/controllers/server-led-controller.js'; ++import kvm_controller from './server-control/controllers/kvm-controller.js'; + + import server_health_index from './server-health/index.js'; + import inventory_overview_controller from './server-health/controllers/inventory-overview-controller.js'; +diff --git a/app/server-control/controllers/kvm-controller.html b/app/server-control/controllers/kvm-controller.html +new file mode 100644 +index 000000000000..40e4d97454bc +--- /dev/null ++++ b/app/server-control/controllers/kvm-controller.html +@@ -0,0 +1,5 @@ ++<div id="noVNC_container"> ++ <div id="noVNC_status_bar"> ++ <div id="noVNC_left_dummy_elem"></div> ++ </div> ++</div> +diff --git a/app/server-control/controllers/kvm-controller.js b/app/server-control/controllers/kvm-controller.js +new file mode 100644 +index 000000000000..a43f169ddf19 +--- /dev/null ++++ b/app/server-control/controllers/kvm-controller.js +@@ -0,0 +1,55 @@ ++/** ++ * Controller for KVM (Kernel-based Virtual Machine) ++ * ++ * @module app/serverControl ++ * @exports kvmController ++ * @name kvmController ++ */ ++ ++import RFB from '@novnc/novnc/core/rfb.js'; ++ ++window.angular && (function(angular) { ++ 'use strict'; ++ ++ angular.module('app.serverControl').controller('kvmController', [ ++ '$scope', '$location', '$log', ++ function($scope, $location, $log) { ++ var rfb; ++ ++ $scope.$on('$destroy', function() { ++ if (rfb) { ++ rfb.disconnect(); ++ } ++ }); ++ ++ function sendCtrlAltDel() { ++ rfb.sendCtrlAltDel(); ++ return false; ++ }; ++ ++ function connected(e) { ++ $log.debug('RFB Connected'); ++ } ++ function disconnected(e) { ++ $log.debug('RFB disconnected'); ++ } ++ ++ var host = $location.host(); ++ var port = $location.port(); ++ var target = ++ angular.element(document.querySelector('#noVNC_container'))[0]; ++ ++ try { ++ rfb = new RFB(target, 'wss://' + host + ':' + port + '/kvm/0', {}); ++ ++ rfb.addEventListener('connect', connected); ++ rfb.addEventListener('disconnect', disconnected); ++ } catch (exc) { ++ $log.error(exc); ++ updateState( ++ null, 'fatal', null, 'Unable to create RFB client -- ' + exc); ++ return; // don't continue trying to connect ++ }; ++ } ++ ]); ++})(angular); +diff --git a/app/server-control/index.js b/app/server-control/index.js +index 739bd1eb8ad9..1b8aad50b702 100644 +--- a/app/server-control/index.js ++++ b/app/server-control/index.js +@@ -48,6 +48,11 @@ window.angular && (function(angular) { + 'controller': 'remoteConsoleWindowController', + authenticated: true + }) ++ .when('/server-control/kvm', { ++ 'template': require('./controllers/kvm-controller.html'), ++ 'controller': 'kvmController', ++ authenticated: true ++ }) + .when('/server-control', { + 'template': + require('./controllers/power-operations-controller.html'), +diff --git a/app/server-control/styles/index.scss b/app/server-control/styles/index.scss +index f6b15ab6afc9..5e8a99580894 100644 +--- a/app/server-control/styles/index.scss ++++ b/app/server-control/styles/index.scss +@@ -3,3 +3,4 @@ + @import "./remote-console.scss"; + @import "./server-led.scss"; + @import "./power-usage.scss"; ++@import "./kvm.scss"; +diff --git a/app/server-control/styles/kvm.scss b/app/server-control/styles/kvm.scss +new file mode 100644 +index 000000000000..2f9e2c0c9f37 +--- /dev/null ++++ b/app/server-control/styles/kvm.scss +@@ -0,0 +1,11 @@ ++ ++.noNVC_shown { ++ display: inline; ++} ++.noVNC_hidden { ++ display: none; ++} ++ ++#noVNC_left_dummy_elem { ++ flex: 1; ++} +diff --git a/package-lock.json b/package-lock.json +index 2d9d31b21968..103c9b84b933 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -807,6 +807,11 @@ + "to-fast-properties": "2.0.0" + } + }, ++ "@novnc/novnc": { ++ "version": "1.0.0", ++ "resolved": "https://registry.npmjs.org/@novnc/novnc/-/novnc-1.0.0.tgz", ++ "integrity": "sha1-drDonm+HOMqBVBlbr1uOaoC8kQU=" ++ }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", +diff --git a/package.json b/package.json +index 35c6b78e320c..fd253cbb2f02 100644 +--- a/package.json ++++ b/package.json +@@ -30,6 +30,7 @@ + "dependencies": { + "angular": "^1.7.5", + "angular-animate": "^1.7.5", ++ "@novnc/novnc": "^1.0.0", + "angular-clipboard": "^1.6.2", + "angular-cookies": "^1.7.5", + "angular-messages": "^1.7.6", +diff --git a/webpack.config.js b/webpack.config.js +index 91cbea8f2952..6c8667cbbc98 100644 +--- a/webpack.config.js ++++ b/webpack.config.js +@@ -113,7 +113,11 @@ module.exports = (env, options) => { + 'base-uri': '\'self\'', + 'object-src': '\'none\'', + 'script-src': ['\'self\''], +- 'style-src': ['\'self\''] ++ 'style-src': ['\'self\''], ++ // KVM requires image buffers from data: payloads, so allow that in ++ // img-src ++ // https://stackoverflow.com/questions/18447970/content-security-policy-data-not-working-for-base64-images-in-chrome-28 ++ 'img-src': ['\'self\'', 'data:'], + }), + new MiniCssExtractPlugin(), + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0007-Fix-some-page-keeps-loading-on-IE11.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0007-Fix-some-page-keeps-loading-on-IE11.patch new file mode 100644 index 000000000..31d810cb6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0007-Fix-some-page-keeps-loading-on-IE11.patch @@ -0,0 +1,68 @@ +From 5a3750377629725890f949d68e7e0ba7d4752825 Mon Sep 17 00:00:00 2001 +From: Mirage Su <mirage.su@mic.com.tw> +Date: Fri, 8 Mar 2019 17:12:46 +0800 +Subject: [PATCH] Fix some page keeps loading on IE11 + +--- + app/index.js | 1 + + package-lock.json | 19 +++++++++++++++++++ + package.json | 1 + + 3 files changed, 21 insertions(+) + mode change 100644 => 100755 package-lock.json + +diff --git a/app/index.js b/app/index.js +index c9fed83..c51203e 100644 +--- a/app/index.js ++++ b/app/index.js +@@ -8,6 +8,7 @@ + */ + import 'angular/angular-csp.css'; + import 'bootstrap/dist/css/bootstrap.css'; ++import 'babel-polyfill'; + + import angular from 'angular'; + import angular_animate from 'angular-animate'; +diff --git a/package-lock.json b/package-lock.json +old mode 100644 +new mode 100755 +index 2d9d31b..61e9787 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1500,6 +1500,25 @@ + "babel-runtime": "6.26.0" + } + }, ++ "babel-polyfill": { ++ "version": "6.26.0", ++ "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", ++ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", ++ "dev": true, ++ "requires": { ++ "babel-runtime": "^6.26.0", ++ "core-js": "^2.5.0", ++ "regenerator-runtime": "^0.10.5" ++ }, ++ "dependencies": { ++ "regenerator-runtime": { ++ "version": "0.10.5", ++ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", ++ "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", ++ "dev": true ++ } ++ } ++ }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", +diff --git a/package.json b/package.json +index 35c6b78..1bdf934 100644 +--- a/package.json ++++ b/package.json +@@ -50,6 +50,7 @@ + "autoprefixer": "9.1.5", + "babel-core": "6.26.3", + "babel-loader": "8.0.2", ++ "babel-polyfill": "^6.26.0", + "compression-webpack-plugin": "2.0.0", + "copy-webpack-plugin": "4.5.2", + "csp-html-webpack-plugin": "^2.5.0", diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/config.json b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/config.json new file mode 100644 index 000000000..1543a6249 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/config.json @@ -0,0 +1,8 @@ +{ + "customKeyEnable": true, + "keyType" : "VT100+", + "customConsoleDisplaySize": { + "width": 100, + "height": 32 + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend new file mode 100644 index 000000000..9caa37767 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend @@ -0,0 +1,13 @@ +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +#SRC_URI = "git://github.com/openbmc/phosphor-webui.git" +SRCREV = "a2e36e0f479d1a9fa2b6d26448d5e070aea7259b" + +SRC_URI += "file://0001-Implement-KVM-in-webui.patch \ + file://config.json \ + file://0007-Fix-some-page-keeps-loading-on-IE11.patch \ + " + +do_compile_prepend() { + cp -r ${WORKDIR}/config.json ${S}/ +} diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/ldb.sh b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/ldb.sh new file mode 100644 index 000000000..176bfd7ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/ldb.sh @@ -0,0 +1 @@ +export LDB_MODULES_PATH=/usr/lib/ldb diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/locked_groups b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/locked_groups new file mode 100644 index 000000000..7c189e231 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/locked_groups @@ -0,0 +1 @@ +sssd_locked diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/nscd.conf b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/nscd.conf new file mode 100644 index 000000000..d2ffe5ddc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/nscd.conf @@ -0,0 +1,2 @@ +enable-cache passwd no +enable-cache group no
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.conf b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.conf new file mode 100644 index 000000000..7a2786bee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.conf @@ -0,0 +1,16 @@ +[sssd] +domains = LOCAL +services = nss, pam +config_file_version = 2 + +[nss] +enum_cache_timeout = 1 +filter_groups = root +filter_users = root + +[pam] + +[domain/LOCAL] +enumerate = true +id_provider = local +auth_provider = local diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.service b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.service new file mode 100644 index 000000000..fe2bcf8b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.service @@ -0,0 +1,15 @@ +[Unit] +Description=System Security Services Daemon +# SSSD must be running before we permit user sessions +Before=systemd-user-sessions.service nss-user-lookup.target +Wants=nss-user-lookup.target + +[Service] +Environment=LDB_MODULES_PATH=/usr/lib/ldb DEBUG_LOGGER=-f +ExecStart=/usr/sbin/sssd $DEBUG_LOGGER +Type=simple +Restart=always +PIDFile=/var/run/sssd.pid + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/sssd_%.bbappend b/meta-openbmc-mods/meta-common/recipes-security/sssd/sssd_%.bbappend new file mode 100644 index 000000000..03965ce72 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/sssd_%.bbappend @@ -0,0 +1,26 @@ +inherit obmc-phosphor-systemd + +FILESEXTRAPATHS_append := "${THISDIR}/files:" +SRC_URI += "file://sssd.conf \ + file://nscd.conf \ + file://locked_groups \ + file://ldb.sh \ + " + +PACKAGECONFIG += " systemd " +SYSTEMD_AUTO_ENABLE = "enable" + +EXTRA_OECONF += " --enable-pammoddir=${base_libdir}/security" + +do_install_append() { + # sssd creates also the /var/run link. Need to remove it to avoid conflicts + # with the one created by base-files recipe. + rm -rf ${D}/var/run + install -m 600 ${WORKDIR}/locked_groups ${D}/${sysconfdir}/${BPN} + install -m 600 ${WORKDIR}/nscd.conf ${D}/${sysconfdir} + install -d ${D}${sysconfdir}/profile.d + install -m 0644 ${WORKDIR}/ldb.sh ${D}${sysconfdir}/profile.d +} + +FILES_${PN} += " /lib/security/pam_sss.so " + diff --git a/meta-openbmc-mods/meta-common/recipes-support/boost/boost/0001-Close-the-read-pipe-after-_read_error-completes.patch b/meta-openbmc-mods/meta-common/recipes-support/boost/boost/0001-Close-the-read-pipe-after-_read_error-completes.patch new file mode 100644 index 000000000..80b1084ad --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-support/boost/boost/0001-Close-the-read-pipe-after-_read_error-completes.patch @@ -0,0 +1,45 @@ +From a035b099050e0f6e953001ce5b7f415043a12ec6 Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@linux.intel.com> +Date: Wed, 20 Mar 2019 18:20:12 -0700 +Subject: [PATCH] Close the read pipe after _read_error() completes + +There are exit conditions in _read_error() where the pipe does +not get closed resulting in a file descriptor leak in the +parent process after the child exits. + +This change moves the responsibility to close the pipe out of +_read_error() to the caller of _read_error() which aligns +with the behavior of _write_error(). + +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +--- + boost/process/detail/posix/executor.hpp | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/boost/process/detail/posix/executor.hpp b/boost/process/detail/posix/executor.hpp +index 1390a58..661fbc5 100644 +--- a/boost/process/detail/posix/executor.hpp ++++ b/boost/process/detail/posix/executor.hpp +@@ -296,11 +296,9 @@ class executor + //EAGAIN not yet forked, EINTR interrupted, i.e. try again + else if ((err != EAGAIN ) && (err != EINTR)) + { +- ::close(source); + set_error(std::error_code(err, std::system_category()), "Error read pipe"); + } + } +- ::close(source); + set_error(ec, std::move(msg)); + } + +@@ -429,6 +427,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_) + + ::close(p[1]); + _read_error(p[0]); ++ ::close(p[0]); + + if (_ec) + { +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-support/boost/boost_%.bbappend b/meta-openbmc-mods/meta-common/recipes-support/boost/boost_%.bbappend new file mode 100644 index 000000000..d42d7ff6d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-support/boost/boost_%.bbappend @@ -0,0 +1,3 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0001-Close-the-read-pipe-after-_read_error-completes.patch" diff --git a/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod/0001-Add-pass-through-setting-in-gpioset.patch b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod/0001-Add-pass-through-setting-in-gpioset.patch new file mode 100644 index 000000000..b90d42ca3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod/0001-Add-pass-through-setting-in-gpioset.patch @@ -0,0 +1,265 @@ +From 380a088dbe9fdd69e98bf6da8086e3d664da8338 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Thu, 31 Jan 2019 22:28:48 +0800 +Subject: [PATCH] Add pass through setting in gpioset + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + aclocal.m4 | 2 +- + configure | 2 +- + include/gpiod.h | 10 ++++++++-- + src/lib/core.c | 13 +++++++------ + src/lib/ctxless.c | 8 ++++---- + src/lib/helpers.c | 3 ++- + src/tools/gpioset.c | 13 +++++++++---- + tests/tests-ctxless.c | 2 +- + 8 files changed, 33 insertions(+), 20 deletions(-) + +diff --git a/aclocal.m4 b/aclocal.m4 +index b0db596..a5e28ad 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -911,7 +911,7 @@ AS_VAR_IF([$1], [""], [$5], [$4])dnl + # generated from the m4 files accompanying Automake X.Y. + # (This private macro should not be called outside this file.) + AC_DEFUN([AM_AUTOMAKE_VERSION], +-[am__api_version='1.15' ++[am__api_version='1.16' + dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to + dnl require some minimum version. Point them to the right macro. + m4_if([$1], [1.15], [], +diff --git a/configure b/configure +index 3d44cd1..d218338 100755 +--- a/configure ++++ b/configure +@@ -2477,7 +2477,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +-am__api_version='1.15' ++am__api_version='1.16' + + # Find a good install program. We prefer a C program (faster), + # so one script is as good as another. But avoid the broken or +diff --git a/include/gpiod.h b/include/gpiod.h +index ccff977..0f935e6 100644 +--- a/include/gpiod.h ++++ b/include/gpiod.h +@@ -121,6 +121,7 @@ typedef void (*gpiod_ctxless_set_value_cb)(void *); + * @param offset The offset of the GPIO line. + * @param value New value (0 or 1). + * @param active_low The active state of this line - true if low. ++ * @param pass_through The pass-through state of the lines - true if enabled. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * the value. Users can use this, for example, to pause the execution +@@ -129,7 +130,7 @@ typedef void (*gpiod_ctxless_set_value_cb)(void *); + * @return 0 if the operation succeeds, -1 on error. + */ + int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data) GPIOD_API; + +@@ -140,6 +141,7 @@ int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, + * @param values Array of integers containing new values. + * @param num_lines Number of lines, must be > 0. + * @param active_low The active state of the lines - true if low. ++ * @param pass_through The pass-through state of the lines - true if enabled. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * all values. Works the same as in ::gpiod_ctxless_set_value. +@@ -149,7 +151,7 @@ int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, + int gpiod_ctxless_set_value_multiple(const char *device, + const unsigned int *offsets, + const int *values, unsigned int num_lines, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data) GPIOD_API; + +@@ -766,6 +768,8 @@ enum { + /**< Request the line(s) for reading the GPIO line state. */ + GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, + /**< Request the line(s) for setting the GPIO line state. */ ++ GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH, ++ /**< Request the line(s) for setting the GPIO line state. */ + GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, + /**< Monitor both types of events. */ + GPIOD_LINE_REQUEST_EVENT_RISING_EDGE, +@@ -784,6 +788,8 @@ enum { + /**< The line is an open-source port. */ + GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW = GPIOD_BIT(2), + /**< The active state of the line is low (high is the default). */ ++ GPIOD_LINE_REQUEST_FLAG_PASS_THROUGH = GPIOD_BIT(5), ++ /**< The line is a pass-through port*/ + }; + + /** +diff --git a/src/lib/core.c b/src/lib/core.c +index 4f273e3..feec362 100644 +--- a/src/lib/core.c ++++ b/src/lib/core.c +@@ -472,7 +472,6 @@ static int line_request_values(struct gpiod_line_bulk *bulk, + struct gpiohandle_request req; + unsigned int i; + int rv, fd; +- + if ((config->request_type != GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) && + (config->flags & (GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN | + GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE))) { +@@ -499,14 +498,15 @@ static int line_request_values(struct gpiod_line_bulk *bulk, + req.flags |= GPIOHANDLE_REQUEST_INPUT; + else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) + req.flags |= GPIOHANDLE_REQUEST_OUTPUT; +- ++ else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH) ++ req.flags |= GPIOHANDLE_REQUEST_PASS_THROUGH; + req.lines = gpiod_line_bulk_num_lines(bulk); + + gpiod_line_bulk_foreach_line_off(bulk, line, i) { + req.lineoffsets[i] = gpiod_line_offset(line); +- if (config->request_type == +- GPIOD_LINE_REQUEST_DIRECTION_OUTPUT && +- default_vals) ++ if ((config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT || ++ config->request_type == GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH)&& ++ default_vals) + req.default_values[i] = !!default_vals[i]; + } + +@@ -617,7 +617,8 @@ static bool line_request_is_direction(int request) + { + return request == GPIOD_LINE_REQUEST_DIRECTION_AS_IS || + request == GPIOD_LINE_REQUEST_DIRECTION_INPUT || +- request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; ++ request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT || ++ request == GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH; + } + + static bool line_request_is_events(int request) +diff --git a/src/lib/ctxless.c b/src/lib/ctxless.c +index 0009504..a16207e 100644 +--- a/src/lib/ctxless.c ++++ b/src/lib/ctxless.c +@@ -76,17 +76,17 @@ int gpiod_ctxless_get_value_multiple(const char *device, + } + + int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, void *data) + { + return gpiod_ctxless_set_value_multiple(device, &offset, &value, 1, +- active_low, consumer, cb, data); ++ active_low, pass_through, consumer, cb, data); + } + + int gpiod_ctxless_set_value_multiple(const char *device, + const unsigned int *offsets, + const int *values, unsigned int num_lines, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, void *data) + { + struct gpiod_line_bulk bulk; +@@ -117,7 +117,7 @@ int gpiod_ctxless_set_value_multiple(const char *device, + } + + flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0; +- ++ flags |= pass_through ? GPIOD_LINE_REQUEST_FLAG_PASS_THROUGH : 0; + status = gpiod_line_request_bulk_output_flags(&bulk, consumer, + flags, values); + if (status < 0) { +diff --git a/src/lib/helpers.c b/src/lib/helpers.c +index 80b8eff..807fb93 100644 +--- a/src/lib/helpers.c ++++ b/src/lib/helpers.c +@@ -362,7 +362,8 @@ int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk, + .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, + .flags = flags, + }; +- ++ if (flags & GPIOD_LINE_REQUEST_FLAG_PASS_THROUGH) ++ config.request_type = GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH; + return gpiod_line_request_bulk(bulk, &config, default_vals); + } + +diff --git a/src/tools/gpioset.c b/src/tools/gpioset.c +index fb012fa..d5f0b77 100644 +--- a/src/tools/gpioset.c ++++ b/src/tools/gpioset.c +@@ -22,7 +22,8 @@ + static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, +- { "active-low", no_argument, NULL, 'l' }, ++ { "active-low", no_argument, NULL, 'l' }, ++ { "pass-through", no_argument, NULL, 'p' }, + { "mode", required_argument, NULL, 'm' }, + { "sec", required_argument, NULL, 's' }, + { "usec", required_argument, NULL, 'u' }, +@@ -30,7 +31,7 @@ static const struct option longopts[] = { + { GETOPT_NULL_LONGOPT }, + }; + +-static const char *const shortopts = "+hvlm:s:u:b"; ++static const char *const shortopts = "+hvlpm:s:u:b"; + + static void print_help(void) + { +@@ -40,8 +41,9 @@ static void print_help(void) + printf("\n"); + printf("Options:\n"); + printf(" -h, --help:\t\tdisplay this message and exit\n"); +- printf(" -v, --version:\tdisplay the version and exit\n"); + printf(" -l, --active-low:\tset the line active state to low\n"); ++ printf(" -v, --version:\tdisplay the version and exit\n"); ++ printf(" -p, --pass-through:\tset it to pass through mode\n"); + printf(" -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n"); + printf(" tell the program what to do after setting values\n"); + printf(" -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n"); +@@ -179,6 +181,7 @@ int main(int argc, char **argv) + int *values, status, optc, opti; + struct callback_data cbdata; + bool active_low = false; ++ bool pass_through = false; + char *device, *end; + + memset(&cbdata, 0, sizeof(cbdata)); +@@ -197,6 +200,8 @@ int main(int argc, char **argv) + return EXIT_SUCCESS; + case 'l': + active_low = true; ++ case 'p': ++ pass_through = true; + break; + case 'm': + mode = parse_mode(optarg); +@@ -263,7 +268,7 @@ int main(int argc, char **argv) + } + + status = gpiod_ctxless_set_value_multiple(device, offsets, values, +- num_lines, active_low, ++ num_lines, active_low, pass_through, + "gpioset", mode->callback, + &cbdata); + if (status < 0) +diff --git a/tests/tests-ctxless.c b/tests/tests-ctxless.c +index ea9403d..228c49d 100644 +--- a/tests/tests-ctxless.c ++++ b/tests/tests-ctxless.c +@@ -20,7 +20,7 @@ static void ctxless_set_get_value(void) + TEST_ASSERT_EQ(ret, 0); + + ret = gpiod_ctxless_set_value(test_chip_name(0), 3, 1, +- false, TEST_CONSUMER, NULL, NULL); ++ false, false, TEST_CONSUMER, NULL, NULL); + TEST_ASSERT_RET_OK(ret); + + ret = gpiod_ctxless_get_value(test_chip_name(0), 3, +-- +2.19.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod_%.bbappend b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod_%.bbappend new file mode 100644 index 000000000..aa28e8d85 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod_%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://0001-Add-pass-through-setting-in-gpioset.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/beeper-test.bb b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/beeper-test.bb new file mode 100644 index 000000000..51cda82e9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/beeper-test.bb @@ -0,0 +1,17 @@ +SUMMARY = "Beeper Test App" +DESCRIPTION = "Beeper Test Application for pwm-beeper" + +inherit cmake + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM += "\ + file://beeper-test.cpp;beginline=2;endline=14;md5=c451359f18a13ee69602afce1588c01a \ + " + +SRC_URI = "\ + file://CMakeLists.txt \ + file://beeper-test.cpp \ + " + +S = "${WORKDIR}" + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/.clang-format b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/.clang-format new file mode 100644 index 000000000..ea71ad6e1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/.clang-format @@ -0,0 +1,99 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/CMakeLists.txt new file mode 100644 index 000000000..81a0c7e81 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(beeper-test CXX) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_executable(beeper-test beeper-test.cpp) +install(TARGETS beeper-test DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/beeper-test.cpp b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/beeper-test.cpp new file mode 100644 index 000000000..31dafdd88 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/beeper-test.cpp @@ -0,0 +1,81 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Abstract: pwm-beeper test application +// +*/ + +#include <fcntl.h> +#include <linux/input.h> +#include <stdio.h> +#include <unistd.h> + +#include <cstring> +#include <iostream> + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::cout << "usage: <input device> <sequence of 'tone in " + "Hz','duration in ms' pair>\n"; + std::cout << "example: beeper-test /dev/input/event0 " + "2100,100,0,150,2500,50,0,50,2200,100\n"; + return 1; + } + + int fd; + if ((fd = open(argv[1], O_RDWR | O_CLOEXEC)) < 0) + { + perror("Failed to open input device"); + return -1; + } + + struct input_event event; + event.type = EV_SND; + event.code = SND_TONE; + + char* pch = strtok(argv[2], ","); + while (pch != NULL) + { + event.value = atoi(pch); + + pch = strtok(NULL, ","); + if (!pch) + { + std::cerr << "Invalid tone,duration pair\n"; + close(fd); + return -1; + } + + int durationMs = atoi(pch); + + if (write(fd, &event, sizeof(struct input_event)) != + sizeof(struct input_event)) + { + perror("Failed to write a tone sound event"); + close(fd); + return -1; + } + + usleep(durationMs * 1000); + + pch = strtok(NULL, ","); + } + + close(fd); + + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/dimmsensor.bb b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/dimmsensor.bb new file mode 100644 index 000000000..78240c0f4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/dimmsensor.bb @@ -0,0 +1,17 @@ +SUMMARY = "Dimm Sensor" +DESCRIPTION = "Dimm Sensor Executable" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://DimmSensor.cpp \ + " + +LICENSE = "CLOSED" + +S = "${WORKDIR}" + +inherit cmake + +# linux-libc-headers guides this way to include custom uapi headers +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/CMakeLists.txt new file mode 100644 index 000000000..6262f8dee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(dimmsensor CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_executable(dimmsensor DimmSensor.cpp) +install (TARGETS dimmsensor DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/DimmSensor.cpp b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/DimmSensor.cpp new file mode 100644 index 000000000..9cc13c2a5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/DimmSensor.cpp @@ -0,0 +1,143 @@ +#include <fcntl.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <memory> +#include <vector> +#include <linux/aspeed_peci_ioctl.h> + +#define PECI_DIMM_TEMP_REG 0x150 +#define DIMM_DEFAULT_VALUE 0x55 + +class DimmConfig { + public: + uint8_t chanId, bus, device, function, dimmnum; +}; + +#define MAX_BUFFER_SIZE 32 + +// TODO get this from config +auto GetDimmConfig(uint8_t dimm) { + uint8_t chan = dimm / 2; + assert(chan < 6); + + auto ret = std::make_unique<DimmConfig>(); + + ret->chanId = chan; + ret->dimmnum = 2; + ret->bus = 2; + switch (chan) { + case 0: + ret->device = 10; + ret->function = 2; + break; + case 1: + ret->device = 10; + ret->function = 6; + break; + case 2: + ret->device = 11; + ret->function = 2; + break; + case 3: + ret->device = 12; + ret->function = 2; + break; + case 4: + ret->device = 12; + ret->function = 6; + break; + case 5: + ret->device = 13; + ret->function = 2; + break; + default: + assert(0); + break; + } + + return ret; +} + +// returns read vector on success, empty vector on failure +auto peci_config_local(uint8_t u8target, uint8_t u8bus, uint8_t u8device, + uint8_t u8fcn, uint16_t u16reg, uint8_t u8readlen) { + auto msg = std::make_unique<peci_xfer_msg>(); + uint32_t u32Address; + int fd; + std::vector<uint8_t> ret; + + u32Address = u16reg; + u32Address |= u8fcn << 12; + u32Address |= u8device << 15; + u32Address |= u8bus << 20; + + msg->client_addr = u8target; + msg->tx_len = RDPCICFGLOCAL_WRITE_LEN; + msg->rx_len = RDPCICFGLOCAL_READ_LEN_BASE + u8readlen; + + msg->tx_buf[0] = RDPCICFGLOCAL_PECI_CMD; + msg->tx_buf[2] = u32Address & 0xFF; + msg->tx_buf[3] = (u32Address >> 8) & 0xFF; + msg->tx_buf[4] = (u32Address >> 16) & 0xFF; + + fd = open("/dev/peci", O_RDWR | O_CLOEXEC); + if (fd >= 0) { + int success = ioctl(fd, PECI_IOC_XFER, msg.get()); + if (success == 0) { + if (DEV_PECI_CC_SUCCESS == msg->rx_buf[0]) { + ret.resize(RDPCICFGLOCAL_READ_LEN_BASE + u8readlen - 1); + memcpy(ret.data(), &(msg->rx_buf[1]), ret.size()); + } + } + close(fd); + } + return ret; +} + +int main(int argc, char** argv) { + if (argc != 3) { + std::cout << argv[0] << " requires 2 arguments: CPUNum, DimmNum.\n"; + return -1; + } + uint8_t cpunum = atoi(argv[1]); + if (cpunum > 3) { + std::cout << cpunum << " greater than cpu max of 3.\n"; + return -1; + } + uint8_t dimmnum = atoi(argv[2]); + if (dimmnum > 11) { + std::cout << dimmnum << " greater than dimm max of 11.\n"; + return -1; + } + + auto dimm_config = GetDimmConfig(dimmnum); + + uint8_t dimmSelect = dimmnum % 2; // dimm 0 or 1 for each config + + auto val = peci_config_local(PECI_BASE_ADDR + cpunum, dimm_config->bus, + dimm_config->device, dimm_config->function, + PECI_DIMM_TEMP_REG + dimmSelect * 4, 4); + + if (!val.size()) { + std::cout << "Peci Error\n"; + return -1; + } + + // TODO dimm offsets needed? + + if (val[0] == 0) + std::cout << "Dimm " << unsigned(dimmnum) << " CPU " << unsigned(cpunum) + << " not populated.\n"; + else if(val[0] == DIMM_DEFAULT_VALUE) + std::cout << "Dimm " << unsigned(dimmnum) << " CPU " << unsigned(cpunum) + << " in illegal state.\n"; + else + std::cout << unsigned(val[0]) << " degrees C.\n"; +} diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/intel-signed-image/files/genimage-si.ini b/meta-openbmc-mods/meta-common/recipes-utilities/intel-signed-image/files/genimage-si.ini new file mode 100644 index 000000000..619570dbb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/intel-signed-image/files/genimage-si.ini @@ -0,0 +1,277 @@ +; +; Copyright 2015 Intel Corporation. +; +; The source code, information and material ("Material") contained herein is +; owned by Intel Corporation or its suppliers or licensors, and title to such +; Material remains with Intel Corporation or its suppliers or licensors. The +; Material contains proprietary information of Intel or its suppliers and +; licensors. The Material is protected by worldwide copyright laws and treaty +; provisions. No part of the Material may be used, copied, reproduced, +; modified, published, uploaded, posted, transmitted, distributed or disclosed +; in any way without Intel's prior express written permission. No license under +; any patent, copyright or other intellectual property rights in the Material +; is granted to or conferred upon you, either expressly, by implication, +; inducement, estoppel or otherwise. Any license under such intellectual +; property rights must be express and approved by Intel in writing. + +; +; This file is similar to the config.genimage2 file of previous BMC +; generations but it is not generated. It contains all of the information +; to generate the signed-image variant of the signtool config file. +; + +[GLOBAL] +Major = 0 +Minor = 72 +Output = update.bin +Alloc = 33792K ; 0x2100000 +BlockSize = 64K +Type = 0xffffffff ; generate top level composite image +Locate = 0 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Factory ROM file generation +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +RomOutput = rom-a-only.ima +RomAlloc = 33M +KeyRegion = Certificate +NewKeyRegion = ReplacementCertificate +SecVersion = 0 + + + +; RecoveryOnly items should be place first in the image + +[FRISBEE_ROM] +Major = 1 +Minor = 5 +Type = ROBL +File = frisbee.bin +Locate = 0x00000000 +Alloc = 240K +Compress = 0 +SecVersion = 1 +Unsigned = 1 + +[Certificate] +Locate = 0x3c000 +Type = CERT +Alloc = 8K +Fill = 0xff +Unsigned = 1 +ROMOnly = 1 +SecVersion = 0 + +;[FWPRODUCTID] +;Major = 1 +;Minor = 0 +;Type = FWID +;File = fwproductid.bin +;Locate = 0x0003f000 +;ROMOnly = 1 +;SecVersion = 0 + +[FRISBEE_UPD] +Major = 1 +Minor = 5 +Type = RWBL +File = frisbee.bin +Locate = 0x040000 +Alloc = 256K +Compress = 0 +SecVersion = 1 +IndivSign = 1 +SigOffset = 0x03e000 + +[U-Boot] +Major = 1 +Minor = 5 +Type = UBT\x00 +File = u-boot.bin +Alloc = 256K +Compress = 0 +SecVersion = 1 +IndivSign = 1 +SigOffset = 0x03e000 + +; Linux OS Image +[OSIMAGE] +Major = 1 +Minor = 1 +Type = LKNL +File = uImage +SecVersion = 1 + +[DTB] +Major = 1 +Minor = 1 +Type = DTB\x00 +File = uImage-aspeed-bmc-intel-purley.dtb +SecVersion = 0 + +; Root File System +[ROOT] +Major = 1 +Minor = 1 +Type = RtFS +File = image-rofs.squashfs-xz.u-boot +Load = 0x83000000 +SecVersion = 1 +; +; WWW File System in CRAMFS. +;[WWW] +;Major = 1 +;Minor = 1 +;Type = WWW\x00 +;File = webfs.bin +;BlockDev = 1 +;SecVersion = 1 + +; Replacement certificate for re-keying the BMC +; Will only be added to image if -rk is specified +[ReplacementCertificate] +Major = 1 +Minor = 1 +Type = CRT0 +Alloc = 4K +SecVersion = 0 +Compress = 0 +IndivSign = 1 +Unbound = 1 +Fill = 0xff +SigOffset = 0x800 + +; Manifest goes here +; This gets some special treatment (this needs to match the location +; that Frisbee thinks the manifest is at or it won't boot) +[Manifest] +Major = 0 +Minor = 0 +Type = MFST +Alloc = 4K +Locate = 0x1bff000 +Fill = 0xff +SecVersion = 0 + +; +; NV File System in JFFS2, but it is blank in the ROM version +; and filled in with defaults from the rootfs +[PARAMS] +Major = 1 +Minor = 1 +Type = CONF +Alloc = 4096K +Locate = 0x1c00000 +ROMOnly = 1 +BlockDev = 1 +SecVersion = 1 +Unsigned = 1 + +; notice that these sections have no file +; and are marked as ROMOnly. This forces them +; into the allocation so we don't get overlapping +; sections, but does not actually put anything into +; the rom at build time. + +[UBootEnv] +Major = 1 +Minor = 0 +Type = UENV +; File = ; no file makes this a placeholder +Locate = 0x2000000 +Alloc = 64K +BlockDev = 1 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[FRUData] +Major = 1 +Minor = 0 +Type = FRU\x00 +Alloc = 64K +Locate = 0x2010000 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[SEL] +Major = 1 +Minor = 0 +Type = SEL\x00 +Alloc = 512K +Locate = 0x2020000 +BlockDev = 1 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +; NV filesystem that survives reset mfg defaults. +; OEM Web customization goes here. +[PersistentNV] +Major = 1 +Minor = 0 +Type = PNV\x00 +Alloc = 2048K +Locate = 0x20a0000 +BlockDev = 1 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[RubixLog] +Major = 1 +Minor = 0 +Type = BTLG +Alloc = 4K +Locate = 0x23fe000 +BlockDev = 0 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[BootPointer] +Major = 1 +Minor = 0 +Type = BPTR +Alloc = 4K +Locate = 0x23ff000 +BlockDev = 0 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +; +; Example Section with all possible fields with their default +; values, unless the field is specified mandatory +; +;[EXAMPLE] +;Major = 0 ; Major number of module +;Minor = 0 ; Minor number of module +;Type = TYPE ; four bytes hopefully human readable, +; ; use c-style escapes if non-ascii, \x23\x10\x00\xf3 +; ; or use a number 0xf3001023 +;Alloc = X ; Maximum memory allocated; X = roundup +;File = name ; File containing the module +; ; If Alloc is specified, but no File, a blank +; ; header will be created (only useful for ROMOnly) +;Locate = addr ; Location in Flash; MANDATORY +;ROMOnly = 1 ; if ROMOnly is set and non-zero, this section +; ; will only be present in the ROM image, not the updater image +;RecoveryOnly = 1 ; if RecoveryOnly is set and non-zero, this section +; ; will only be present in the recovery image, not the active image +;BlockDev = 1 ; this will make the signed image code export this +; ; image as a mtd block device at runtime +;Unsigned = 1 ; Do not include this section in signatures (NV data) +;Compress = 1 ; compress image contents (useful for non-compressed items) +;SecVersion = X ; a 16-bit security revision to enforce no downgrades +;IndivSign = 1 ; section is individually signed; do not include in full signature +;Unbound = 1 ; by default, individually signed sections are part of the +; ; full signature too. This makes them independent +;SigOffset = X ; offset within individually-signed section to place signature +;Fill = xx ; fill with pattern xx instead of a file's contents +; +; Note: Numeric values can be represented either by decimal or a +; hexadecimal (Prefixed by 0x) +; Numeric values can either end with K or M to specify in +; KiloBytes or MegaBytes +; diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/CMakeLists.txt new file mode 100644 index 000000000..0c98160b9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(io-app C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +add_executable(io io-app.c) +install (TARGETS io DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/io-app.c b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/io-app.c new file mode 100644 index 000000000..a5ffabd88 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/io-app.c @@ -0,0 +1,665 @@ +/* +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Abstract: map io region to read or write to HW registers +// +*/ + +#define _GNU_SOURCE +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <termios.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> + +static int quiet = 0; + +static int dump(unsigned long phys, void *addr, size_t len) +{ + const uint32_t *ubuf = addr; + unsigned int wd = 0, l, iv; + unsigned char *h, *a; + char line[256]; + static const char itoh[] = "0123456789abcdef"; + + /* 0 1 2 3 4 5 6 + * 0123456789012345678901234567890123456789012345678901234567890123456789 + * 00000000: 00000000 00000000 00000000 00000000 ................ + */ + while (wd < len) + { + memset(line, ' ', sizeof(line)); + sprintf(line, "%08lx: ", phys + (wd*sizeof(uint32_t))); + a = (unsigned char*)&line[47]; + h = (unsigned char*)&line[10]; + for (l=0; l<4 && (l+wd)<len; l++) + { + uint32_t v = *ubuf++; + for (iv=0; iv<sizeof(v); iv++) + { + uint8_t b = v >> (8*((sizeof(v)-1)-iv)); + *h++ = itoh[b>>4]; + *h++ = itoh[b&0xf]; + if (isprint(b)) + *a++ = (char)b; + else + *a++ = '.'; + } + *h++ = ' '; + } + *a++ = '\n'; + *a = 0; + wd += l; + fputs(line, stdout); + } + return wd; +} + + +struct mapping +{ + unsigned long phys; + void *virt; + size_t len; +}; + +static struct mapping *maps = NULL; +static int nr_maps = 0; + +static void unmap_all(void) +{ + int i; + for (i=0; i<nr_maps; i++) + { + if (maps[i].virt) + munmap(maps[i].virt, maps[i].len); + maps[i].virt = NULL; + } +} + +int add_a_map(unsigned long phys, void *virt, size_t len) +{ + void *new_maps; + new_maps = realloc(maps, (nr_maps + 1) * sizeof(struct mapping)); + if (!new_maps) + { + unmap_all(); + munmap(virt, len); + } + else + { + maps = new_maps; + maps[nr_maps].phys = phys; + maps[nr_maps].virt = virt; + maps[nr_maps].len = len; + nr_maps++; + return 0; + } + return -1; +} + +static void *map_fd(int fd, off_t offset, size_t len, int mode, int flags) +{ + void *mapped_at; + unsigned long phys = -1; + + if (offset != -1) + phys = offset; + else + offset = 0; + + mapped_at = mmap(NULL, len, mode, flags, fd, offset); + if (mapped_at != MAP_FAILED) + { + if (add_a_map(phys, mapped_at, len) != 0) + { + mapped_at = MAP_FAILED; + } + } + + return mapped_at; +} + +static void *map_file(const char *fname, size_t *len, int mode, int flags) +{ + int fd; + struct stat sb; + void *ptr; + int fmode; + + if (mode & PROT_WRITE) + fmode = O_RDWR; + else + fmode = O_RDONLY; + + fd = open(fname, fmode); + if (fd < 0) + return MAP_FAILED; + + if (*len == 0) + { + fstat(fd, &sb); + *len = sb.st_size; + } + ptr = map_fd(fd, -1, *len, mode, flags); + close(fd); + return ptr; +} + +static int load_maps(const char *cmap_str, size_t mlen) +{ + char *tmp_sa = NULL, *tmp_sl = NULL, *endptr = NULL; + const void *mapped = NULL; + int ret = 0; + const char *delim = "\r\n\t ,"; + unsigned long addr; + size_t len; + char *map_str = NULL, *paddr = NULL, *plen = NULL; + int fd; + + fd = open("/dev/mem", O_RDWR); + if (fd < 0) + { + return -1; + } + + len = strlen(cmap_str); + map_str = (char *)malloc(len + 1); + if (!map_str) + { + close(fd); + return -1; + } + strncpy(map_str, cmap_str, len); + map_str[len] = '\0'; + paddr = strtok_r(map_str, delim, &tmp_sa); + while (paddr) + { + /* find the next comma or newline */ + if (!strtok_r(paddr, ":", &tmp_sl)) + { + fprintf(stderr, "malformed map string '%s'\n", paddr); + goto _loop; + } + plen = strtok_r(NULL, ":", &tmp_sl); + if (!plen) + { + goto _loop; + } + addr = strtoul(paddr, &endptr, 16); + if (*endptr) + { + fprintf(stderr, "Failed to parse address from '%s'\n", paddr); + ret = -1; + break; + } + len = strtoul(plen, &endptr, 16); + if (*endptr) + { + fprintf(stderr, "Failed to parse len from '%s'\n", plen); + ret = -1; + break; + } + if (MAP_FAILED == (mapped = map_fd(fd, addr, len, PROT_READ|PROT_WRITE, MAP_SHARED))) + { + fprintf(stderr, "Failed to map %lx +%x\n", addr, len); + ret = -1; + break; + } + if (!quiet) + printf("added map: %p (%lx..%lx)\n", mapped, addr, addr+len); +_loop: + paddr = strtok_r(NULL, delim, &tmp_sa); + } + free(map_str); + close(fd); + return ret; +} + +int md(unsigned long addr, uint32_t unused, size_t len) +{ + int i, j; + + (void)unused; + for (i=0; i<nr_maps; i++) + { + if (-1 == maps[i].phys) + continue; + if (maps[i].phys <= addr && + (addr + len * sizeof(uint32_t)) < (maps[i].phys + maps[i].len)) + { + uint32_t *buf, *pv; + + buf = (uint32_t *)malloc(len*sizeof(uint32_t)); + if (!buf) + return 1; + pv = (uint32_t *)(maps[i].virt + (addr - maps[i].phys)); + for (j=0; j<len; j++) + buf[j] = *pv++; + + dump(addr, buf, len); + free(buf); + return 0; + } + } + fprintf(stderr, "%lx +%x not in mapped memory\n", addr, len); + return 1; +} + +int mw(unsigned long addr, uint32_t val, size_t len) +{ + int i, j; + + for (i=0; i<nr_maps; i++) + { + if (-1 == maps[i].phys) + continue; + if (maps[i].phys <= addr && + (addr + len * sizeof(uint32_t)) < (maps[i].phys + maps[i].len)) + { + for (j=0; j<len; j++) + { + *((uint32_t*)(maps[i].virt + (addr - maps[i].phys))) = val; + } + return 0; + } + } + fprintf(stderr, "%lx +%x not in mapped memory\n", addr, len); + return 1; +} + +char *readline(char *buf, size_t len, FILE *f) +{ + int raw = 0; + size_t br = 0; + struct termios tios, orig_tios; + + if (!quiet) + { + /* put terminal in raw mode to get unbuffered io */ + if (tcgetattr(fileno(f), &orig_tios) == 0) + { + tios = orig_tios; + tios.c_iflag |= IGNPAR; + tios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + tios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN); + tios.c_oflag &= ~OPOST; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + tcsetattr(fileno(f), TCSADRAIN, &tios); + raw = 1; + } + } + if (!raw) + { + return fgets(buf, len, f); + } + /* read in bytes one at a time and echo them */ + while (br < (len-1)) + { + int c = fgetc(f); + switch (c) + { + case 3: /* ^C */ + br = 0; + c = '\n'; + break; + case 4: + br = 0; + c = -1; + break; + case '\b': + if (br > 0) + { + fputs("\b \b", stdout); + br--; + } + break; + case '\r': + case '\n': + fputs("\r\n", stdout); + buf[br++] = '\n'; + break; + case ' '...'~': + fputc(c, stdout); + buf[br++] = c; + break; + break; + default: + break; + } + if (c == -1) + { + if (br == 0) + buf = NULL; + break; + } + if (c == '\r' || c == '\n') + break; + } + if (buf) + buf[br] = 0; + + tcsetattr(fileno(f), TCSADRAIN, &orig_tios); + return buf; +} + +#define MAX_LINE_LEN 4096 +int io(void) +{ + const char delim1[] = "\r\n;"; + const char delim2[] = "\t "; + char line[MAX_LINE_LEN]; + char *command, *cmd, *paddr, *pval, *plen, *endptr; + char *tmp_s1, *tmp_s2; + unsigned long addr; + uint32_t val; + size_t len; + int (*fn)(unsigned long, uint32_t, size_t); + + if (!quiet) + fputs("> ", stdout); + while (readline(line, MAX_LINE_LEN, stdin) != NULL && *line) + { + /* read next line or next command (up to newline or ';') */ + command = strtok_r(line, delim1, &tmp_s1); + if (!command || strlen(command) == 0) + goto _command_loop; + while (NULL != command && strlen(command) > 0) + { + cmd = strtok_r(command, delim2, &tmp_s2); + if (!cmd) + goto _cmd_err; + if (cmd[0] == 'q' || cmd[0] == 'Q') + return 0; + paddr = strtok_r(NULL, delim2, &tmp_s2); + if (!paddr) + goto _cmd_err; + addr = strtoul(paddr, &endptr, 16); + if (*endptr) + goto _cmd_err; + fn = NULL; + if (strncmp(cmd, "mw", 3) == 0) + { + fn = mw; + pval = strtok_r(NULL, delim2, &tmp_s2); + if (!pval) + goto _cmd_err; + val = strtoul(pval, &endptr, 16); + if (*endptr) + goto _cmd_err; + len = 1; + } + else if (strncmp(cmd, "md", 3) == 0) + { + fn = md; + len = 0x40; + val = 0; + } + else + { + goto _cmd_err; + } + plen = strtok_r(NULL, delim2, &tmp_s2); + if (plen) + { + len = strtoul(plen, &endptr, 16); + if (*endptr) + goto _cmd_err; + } + + if (fn) + fn(addr, val, len); + + command = strtok_r(NULL, delim1, &tmp_s1); + } +_command_loop: + if (!quiet) + fputs("> ", stderr); + continue; +_cmd_err: + fprintf(stderr, "md addr [len]\nmw addr val [len]\n" + "q[uit] | ctrl-d | ctrl-c to exit\n"); + if (!quiet) + fputs("> ", stderr); + } + return 0; +} + +typedef enum +{ + CPU_NONE = 0, + CPU_PILOT3, + CPU_PILOT4, + CPU_AST2500, + CPU_MAX, +} CPU_TYPE; + +static CPU_TYPE probe_cpu(void) +{ + FILE *f; + char cpuinfo[128]; + static CPU_TYPE this_cpu = CPU_NONE; + + if (CPU_NONE == this_cpu) + { + f = fopen("/sys/firmware/devicetree/base/compatible", "r"); + if (f) { + int br = fread(cpuinfo, 1, sizeof(cpuinfo)-1, f); + if (br > 0) { + cpuinfo[br] = 0; + char *v = cpuinfo; + while (v < (cpuinfo + sizeof(cpuinfo)) && *v) { + if (strncmp("aspeed,ast2500", v, 15) == 0) + { + if (!quiet) + fprintf(stderr, "AST2500\n"); + this_cpu = CPU_AST2500; + } + v += 1 + strnlen(v, sizeof(cpuinfo) - (v - cpuinfo)); + } + } + fclose(f); + } + } + if (CPU_NONE == this_cpu) + { + const char delim[] = "\r\n\t :"; + char *tmp_s; + f = fopen("/proc/cpuinfo", "r"); + if (f != NULL) { + while (fgets(cpuinfo, sizeof(cpuinfo), f)) + { + strtok_r(cpuinfo, delim, &tmp_s); + if (strncmp("Hardware", cpuinfo, 9) == 0) + { + char *v = strtok_r(NULL, delim, &tmp_s); + if (v) + { + if (strncmp("AST2500", v, 8) == 0) + { + if (!quiet) + fprintf(stderr, "AST2500\n"); + this_cpu = CPU_AST2500; + } + else if (strncmp("ASpeed SoC", v, 11) == 0) + { + if (!quiet) + fprintf(stderr, "Found ASpeed SoC\n"); + this_cpu = CPU_AST2500; + } + else if (strncmp("ServerEngines PILOT3", v, 21) == 0) + { + if (!quiet) + fprintf(stderr, "Found PILOT3\n"); + this_cpu = CPU_PILOT3; + } + } + break; + } + } + fclose(f); + } + } + return this_cpu; +} + +static const char *probe_cpu_for_map(void) +{ + switch (probe_cpu()) + { + case CPU_PILOT3: + return "0:2000000,10000000:8000,40000000:43b000"; + case CPU_AST2500: + return "0:4000000,1e600000:1a0000,20000000:4000000"; + default: + return ""; + } +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: io [-c config] [-m map]\n" + " md [-c config] [-m map] <addr> [len]\n" + " mw [-c config] [-m map] <addr> <val> [len]\n\n" + "With: -c config load mappings from file config\n" + " -m map load mappings from string map\n" + " addr, val, len are all hex numbers\n\n" + "When invoked as io, this will start a shell that will\n" + "allow the user to type in md and mw commands much like\n" + "the U-Boot environment. By default, it will map in all\n" + "the addresses for the known processor type.\n\n" + "map string is of the format addr:len[,addr2:len2...]\n" + "config file is of the same format as map string\n" + "but with each mapping on separate lines instead of\n" + "comma separated values\n" + ); + exit(1); +} + +#define shift if (++i >= argc) usage() + +int main(int argc, const char *argv[]) +{ + char *exe_full; + char *exe; + char *endptr; + int i, first_arg = 1; + const char *cfg_file = NULL; + const char *map_str = NULL; + size_t flen = 0; + size_t len = 0; + unsigned long addr; + uint32_t val; + int ret = 0; + + i = 1; + while (i < argc) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'm': + shift; + map_str = argv[i]; + break; + case 'c': + shift; + cfg_file = argv[i]; + break; + default: + usage(); + } + } + else + { + first_arg = i; + break; + } + } + + exe_full = strdup(argv[0]); + if (exe_full != NULL ) + exe = basename(exe_full); + else + return ret; + + if (strncmp(exe, "io", 3) != 0 || !isatty(fileno(stdin))) + quiet = 1; + + if (!map_str) + { + if (!cfg_file) + map_str = probe_cpu_for_map(); + else + map_str = map_file(cfg_file, &flen, PROT_READ, MAP_PRIVATE); + } + else + { + flen = strlen(map_str); + } + if (load_maps(map_str, flen) < 0) + { + fprintf(stderr, "failed to map regions: check map string or config file\n"); + goto _cleanup; + } + + if (strncmp(exe, "md", 3) == 0) + { + len = 0x40; + addr = strtoul(argv[first_arg], &endptr, 16); + if ((first_arg + 1) < argc) + { + len = strtoul(argv[first_arg + 1], &endptr, 16); + } + ret = md(addr, 0, len); + goto _cleanup; + } + + if (strncmp(exe, "mw", 3) == 0) + { + len = 1; + addr = strtoul(argv[first_arg], &endptr, 16); + if ((first_arg + 1) < argc) + { + val = strtoul(argv[first_arg + 1], &endptr, 16); + } + else + { + usage(); + } + if ((first_arg + 2) < argc) + { + len = strtoul(argv[first_arg + 2], &endptr, 16); + } + ret = mw(addr, val, len); + goto _cleanup; + } + + io(); + +_cleanup: + unmap_all(); + free(exe_full); + return ret; +} diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/io-app/io-app.bb b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/io-app.bb new file mode 100644 index 000000000..5ac0f0a75 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/io-app.bb @@ -0,0 +1,17 @@ +SUMMARY = "IO App" +DESCRIPTION = "IO application for accessing memory-mapped IO regions on the BMC" + +inherit cmake + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM += "\ + file://io-app.c;beginline=2;endline=14;md5=639666a0bf40bb717b46b378297eeceb \ + " + +SRC_URI = "\ + file://CMakeLists.txt \ + file://io-app.c \ + " + +S = "${WORKDIR}" + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/CMakeLists.txt new file mode 100644 index 000000000..1cde22b49 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(lpc-cmds C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +add_executable(lpc_cmds lpc_cmds.c) +install (TARGETS lpc_cmds DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_cmds.c b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_cmds.c new file mode 100644 index 000000000..2cdf66d88 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_cmds.c @@ -0,0 +1,479 @@ +/* +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +*/ + +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include "lpc_drv.h" + +#define SIO_DEVICE_NAME "/dev/lpc-sio" +#define KCS_DEVICE_NAME "/dev/ipmi-kcs" +#define MAILBOX_DEVICE_NAME "/dev/aspeed-mbox" +#define SNOOP_DEVICE_NAME "/dev/aspeed-lpc-snoop" + +#define SNOOP_BUF_SIZE 4096 + +#define SUPPORT_KCS_ADDR_CMD 0 +#define SUPPORT_MAILBOX 1 +#define SUPPORT_SNOOP 1 + +/*********************************************************************************/ +static void ProcessKCSReq(int fd, unsigned char *pReq, int ReqLen, int NoPrint) +{ + int i; + unsigned char SendPkt[16]; + + if (!NoPrint) { + printf("\nKCS Request >>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + for (i = 0; i < ReqLen; i++) + printf("%02X ", pReq[i]); + printf("\n======================================\n"); + } + + SendPkt[0] = pReq[0] | 0x04; + SendPkt[1] = pReq[1]; + SendPkt[2] = 0xC1; /* Always Invalid Command */ + + if (!NoPrint) { + printf("\nKCS Response <<<<<<<<<<<<<<<<<<<<<<<<<\n"); + for (i = 0; i < 3; i++) + printf("%02X ", SendPkt[i]); + printf("\n======================================\n"); + } + + write(fd, SendPkt, 3); +} + +static void KCSIfcTask(int KCSIfcIdx, int NoPrint) +{ + int fd; + int RecvPktLen; + char KCSDev[16]; + struct kcs_ioctl_data IOData; + unsigned char RecvPkt[512]; + + snprintf(KCSDev, sizeof(KCSDev), KCS_DEVICE_NAME"%d", KCSIfcIdx); + fd = open(KCSDev, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open KCS device: %s\n", KCSDev); + exit(1); + } + + IOData.cmd = KCS_FORCE_ABORT; + IOData.data = 0; + ioctl(fd, KCS_IOC_COMMAND, &IOData); + + while (1) { + RecvPktLen = read(fd, RecvPkt, sizeof(RecvPkt)); + if (RecvPktLen < 2) + continue; + + ProcessKCSReq(fd, RecvPkt, RecvPktLen, NoPrint); + } +} + +#if SUPPORT_KCS_ADDR_CMD +static void KCSIfcSetAddr(int KCSIfcIdx, unsigned int addr) +{ + int fd; + struct kcs_ioctl_data kcs_data; + char KCSDev[16]; + + snprintf(KCSDev, sizeof(KCSDev), KCS_DEVICE_NAME"%d", KCSIfcIdx); + fd = open(KCSDev, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open KCS device: %s\n", KCSDev); + exit(1); + } + + kcs_data.cmd = KCS_SET_ADDR; + kcs_data.data = addr; + if (ioctl(fd, KCS_IOC_COMMAND, &kcs_data) == 0) + printf("Set KCS%d addr to 0x%X successfully!\n", KCSIfcIdx + 1, addr); + + close(fd); +} + +static void KCSIfcGetAddr(int KCSIfcIdx) +{ + int fd; + struct kcs_ioctl_data kcs_data; + char KCSDev[16]; + + snprintf(KCSDev, sizeof(KCSDev), KCS_DEVICE_NAME"%d", KCSIfcIdx); + fd = open(KCSDev, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open KCS device: %s\n", KCSDev); + exit(1); + } + + kcs_data.cmd = KCS_GET_ADDR; + if (ioctl(fd, KCS_IOC_COMMAND, &kcs_data) == 0) + printf("KCS%d addr is : 0x%X!\n", KCSIfcIdx + 1, kcs_data.data); + + close(fd); +} +#endif + +/*********************************************************************************/ + +#if SUPPORT_SNOOP +static void ReadBiosPOSTCodes(unsigned int if_idx) +{ + char snoop_dev[32]; + int fd; + int i; + unsigned char buf[SNOOP_BUF_SIZE]; + int len; + + snprintf(snoop_dev, sizeof(snoop_dev), SNOOP_DEVICE_NAME"%d", if_idx); + fd = open(snoop_dev, O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s !\n", snoop_dev); + return; + } + + len = read(fd, &buf, sizeof(buf)); + + if (len == 0 || errno == EAGAIN) { + printf("No BIOS POST Codes Found!\n"); + goto out; + } else if (len < 0) { + printf("Failed to read the POST Codes! (%s)\n", + strerror(errno)); + goto out; + } + + printf("BIOS POST Codes in Hex (%d entries):\n", len); + + for (i = 0; i < len; i++) + printf(" %d: %02X\n", i, buf[i]); + + printf("\n"); + +out: + close(fd); +} +#endif + +/*********************************************************************************/ + +static void SIOGetACPIState(unsigned short changed) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_ACPI_STATE; + sio_data.param = changed; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) { + if (changed) + printf("ACPI SLP state is %s!\n", + sio_data.param != 0 ? "Changed" : "Same"); + + if (sio_data.data == ACPI_STATE_S12) + printf("ACPI SLP state --> SLP_12\n"); + else if (sio_data.data == ACPI_STATE_S3I) + printf("ACPI SLP state --> SLP_3I\n"); + else if (sio_data.data == ACPI_STATE_S45) + printf("ACPI SLP state --> SLP_45\n"); + } + + close(fd); +} + +static void SIOGetPWRGDStatus(unsigned short changed) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_PWRGD_STATUS; + sio_data.param = changed; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) { + if (changed) + printf("PWRGD status : %s\n", + sio_data.param != 0 ? "Changed" : "Same"); + + printf("PWRGD status value :%u\n", sio_data.data); + } + + close(fd); +} + +static void SIOGetONCTLStatus(void) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_ONCTL_STATUS; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) + printf("ONCTL status value :%u\n", sio_data.data); + + close(fd); +} + +static void SIOSetONCTLGPIO(unsigned short enable, unsigned int value) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_SET_ONCTL_GPIO; + sio_data.param = enable; + sio_data.data = value; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) + printf("ONCTL GPIO mode setting is Done!\n"); + + close(fd); +} + +static void SIOGetPWRBTNOverride(unsigned short clear) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_PWRBTN_OVERRIDE; + sio_data.param = clear; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) + printf("PWRBTN Override status : %u\n", sio_data.data); + + close(fd); +} + +/*********************************************************************************/ + +#if SUPPORT_MAILBOX +static void MailBoxRead(int num) +{ + int fd; + int len; + uint8_t data; + + fd = open(MAILBOX_DEVICE_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + printf("Error open mailbox\n"); + exit(1); + } + + len = pread(fd, &data, 1, num); + if (len == 0 || errno == EAGAIN) { + printf("No mailbox message found!\n"); + goto out; + } else if (len < 0) { + printf("Error reading from mailbox%d! (%s)\n", num, + strerror(errno)); + goto out; + } + + printf("MailBox%d read value : 0x%02X\n", num, data); + +out: + close(fd); +} + +static void MailBoxWrite(int num, uint8_t value) +{ + int fd; + ssize_t rc; + + fd = open(MAILBOX_DEVICE_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + printf("Error open mailbox\n"); + exit(1); + } + + rc = pwrite(fd, &value, 1, num); + + if (rc == 1) + printf("MailBox%d write value : 0x%02X done!\n", num, value); + else + printf("Error writing to mailbox%d, rc: %d\n", num, rc); + + close(fd); +} +#endif + +/*********************************************************************************/ + +static void usage(void) +{ + printf("Usage:\n" + "\tlpc_cmds sio get_acpi_state\n" + "\tlpc_cmds sio get_acpi_changed\n" + "\tlpc_cmds sio get_pwrgd_status\n" + "\tlpc_cmds sio get_pwrgd_changed\n" + "\tlpc_cmds sio get_onctl_status\n" + "\tlpc_cmds sio set_onctl_gpio_disable\n" + "\tlpc_cmds sio set_onctl_gpio_high\n" + "\tlpc_cmds sio set_onctl_gpio_low\n" + "\tlpc_cmds sio get_pwrbtn_override_status\n" + "\tlpc_cmds sio get_pwrbtn_override_status_clear\n" + "\n" +#if SUPPORT_KCS_ADDR_CMD + "\tlpc_cmds kcs [1 ~ 4] (getaddr / setaddr / quiet)\n" +#else + "\tlpc_cmds kcs [1 ~ 4] (quiet)\n" +#endif +#if SUPPORT_MAILBOX + "\n" + "\tlpc_cmds mailbox read (0 ~ 15)\n" + "\tlpc_cmds mailbox write (0 ~ 15) (0x00 ~ 0xFF)\n" +#endif +#if SUPPORT_SNOOP + "\n" + "\tlpc_cmds snoop [0 ~ 1] read\n" +#endif + ); + + exit(-1); +} + +int main(int argc, char** argv) +{ + char *cmd; + + if (argc < 2) + usage(); + + cmd = argv[1]; + + if (strcmp(cmd, "sio") == 0) { + if (argc < 3) + usage(); + + if (strcmp(argv[2], "get_acpi_state") == 0) + SIOGetACPIState(0); + else if (strcmp(argv[2], "get_acpi_changed") == 0) + SIOGetACPIState(1); + else if (strcmp(argv[2], "get_pwrgd_status") == 0) + SIOGetPWRGDStatus(0); + else if (strcmp(argv[2], "get_pwrgd_changed") == 0) + SIOGetPWRGDStatus(1); + else if (strcmp(argv[2], "get_onctl_status") == 0) + SIOGetONCTLStatus(); + else if (strcmp(argv[2], "set_onctl_gpio_disable") == 0) + SIOSetONCTLGPIO(0, 0); + else if (strcmp(argv[2], "set_onctl_gpio_high") == 0) + SIOSetONCTLGPIO(1, 1); + else if (strcmp(argv[2], "set_onctl_gpio_low") == 0) + SIOSetONCTLGPIO(1, 0); + else if (strcmp(argv[2], "get_pwrbtn_override_status") == 0) + SIOGetPWRBTNOverride(0); + else if (strcmp(argv[2], "get_pwrbtn_override_status_clear") == 0) + SIOGetPWRBTNOverride(1); + } else if (strcmp(cmd, "kcs") == 0) { + int ifc; + + if (argc < 3) + usage(); + + ifc = atoi(argv[2]); + if (ifc < 1 || ifc > 4) /* ipmi-kcs1 ~ ipmi-kcs4 */ + usage(); + + if (argc == 3) + KCSIfcTask(ifc, 0); + else if (argc == 4 && strcmp(argv[3], "quiet") == 0) + KCSIfcTask(ifc, 1); +#if SUPPORT_KCS_ADDR_CMD + else if (argc == 4 && strcmp(argv[3], "getaddr") == 0) + KCSIfcGetAddr(ifc); + else if (argc == 5 && strcmp(argv[3], "setaddr") == 0) + KCSIfcSetAddr(ifc, strtoul(argv[4], NULL, 16)); +#endif +#if SUPPORT_MAILBOX + } else if (strcmp(cmd, "mailbox") == 0) { + if (argc < 4) + usage(); + + if (strcmp(argv[2], "read") == 0) { + MailBoxRead(atoi(argv[3])); + } else { + if (argc < 5) + usage(); + MailBoxWrite(atoi(argv[3]), strtoul(argv[4], NULL, 16)); + } +#endif +#if SUPPORT_SNOOP + } else if (strcmp(cmd, "snoop") == 0) { + int ifc; + + if (argc < 3) + usage(); + + ifc = atoi(argv[2]); + if (ifc < 0 || ifc > 1) /* snoop0 ~ snoop1 */ + usage(); + + if (strcmp(argv[3], "read") == 0) + ReadBiosPOSTCodes(ifc); + else + usage(); +#endif + } + + return 0; +} + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_drv.h b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_drv.h new file mode 100644 index 000000000..56c79d1c1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_drv.h @@ -0,0 +1,78 @@ +/* +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +*/ + +#ifndef __LPC_DRV_H__ +#define __LPC_DRV_H__ + +#define LPC_DEV_MAJOR 250 +#define LPC_DEV_MINOR 0 + +/***********************************************************************************/ + +enum KCS_CMD { + KCS_SET_ADDR = 0, + KCS_GET_ADDR, + KCS_SMS_ATN, + KCS_FORCE_ABORT, +}; + +struct kcs_ioctl_data { + unsigned int cmd; + unsigned int data; +}; + +#define KCS_IOC_BASE 'K' +#define KCS_IOC_COMMAND _IOWR(KCS_IOC_BASE, 1, struct kcs_ioctl_data) + +/***********************************************************************************/ + +enum ACPI_SLP_STATE { + ACPI_STATE_S12 = 1, + ACPI_STATE_S3I, + ACPI_STATE_S45 +}; + +/* SWC & ACPI for SuperIO IOCTL */ +enum SIO_CMD { + SIO_GET_ACPI_STATE = 0, + SIO_GET_PWRGD_STATUS, + SIO_GET_ONCTL_STATUS, + SIO_SET_ONCTL_GPIO, + SIO_GET_PWRBTN_OVERRIDE, + SIO_GET_PFAIL_STATUS, /* Start from AC Loss */ + + SIO_MAX_CMD +}; + +struct sio_ioctl_data { + unsigned short sio_cmd; + unsigned short param; + unsigned int data; +}; + +#define SIO_IOC_BASE 'P' +#define SIO_IOC_COMMAND _IOWR(SIO_IOC_BASE, 1, struct sio_ioctl_data) + +/***********************************************************************************/ + +#define MAX_MAILBOX_NUM 16 + +/***********************************************************************************/ + +#endif + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/lpc-cmds.bb b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/lpc-cmds.bb new file mode 100644 index 000000000..2fbb1eb2f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/lpc-cmds.bb @@ -0,0 +1,16 @@ +SUMMARY = "LPC tools" +DESCRIPTION = "command tool for LPC interface test on the BMC" + +inherit cmake + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${PHOSPHORBASE}/LICENSE;md5=19407077e42b1ba3d653da313f1f5b4e" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://lpc_drv.h \ + file://lpc_cmds.c \ + " + +S = "${WORKDIR}" + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/files/peci-hwmon-test.py b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/files/peci-hwmon-test.py new file mode 100755 index 000000000..977fcd3a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/files/peci-hwmon-test.py @@ -0,0 +1,144 @@ +#!/usr/bin/python + +import glob +import os +import sys +import time + +if len(sys.argv) == 1: + cpuno = 0 +else: + cpuno = int(sys.argv[1]) + +hwmon_path = '/sys/class/hwmon' +cputemp_name_match = '{}{}'.format('peci_cputemp.cpu', cpuno) +dimmtemp_name_match = '{}{}'.format('peci_dimmtemp.cpu', cpuno) + +dimmtemp_path = '' + +os.chdir(hwmon_path) + +for dirpath, dirnames, files in os.walk(hwmon_path): + for d in dirnames: + try: + with open('{}/{}'.format(d, 'name')) as f: + hwmon_name = f.read().strip() + if hwmon_name == cputemp_name_match: + cputemp_path = os.path.abspath(d) + cputemp_name = hwmon_name + elif hwmon_name == dimmtemp_name_match: + dimmtemp_path = os.path.abspath(d) + dimmtemp_name = hwmon_name + except: + continue + +if not cputemp_path: + print "Can't find the " + cputemp_name_match + quit() + +try: + while True: + os.system('clear') + os.chdir(cputemp_path) + + print '{}/{}: {}'.format(hwmon_path, cputemp_path, cputemp_name) + if dimmtemp_path: + print '{}/{}: {}'.format(hwmon_path, dimmtemp_path, dimmtemp_name) + + print + print 'Package temperature' + for input in glob.glob('temp[1-5]_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print '{:11s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000)) + + print + print 'Core temperature' + count = 0 + for input in glob.glob('temp[!1-5]_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print ('{:9s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000))), + count += 1 + if count % 3 == 0: + print + else: + print ('\t'), + for input in glob.glob('temp??_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print ('{:9s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000))), + count += 1 + if count % 3 == 0: + print + else: + print ('\t'), + print + + if dimmtemp_path: + os.chdir(dimmtemp_path) + print + print 'DIMM temperature' + count = 0 + for input in glob.glob('temp*_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print ('{:9s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000))), + count += 1 + if count % 3 == 0: + print + else: + print ('\t'), + print + else: + os.chdir(hwmon_path) + for dirpath, dirnames, files in os.walk(hwmon_path): + for d in dirnames: + try: + with open('{}/{}'.format(d, 'name')) as f: + hwmon_name = f.read().strip() + if hwmon_name == dimmtemp_name_match: + dimmtemp_path = os.path.abspath(d) + dimmtemp_name = hwmon_name + except: + continue + + time.sleep(1) +except KeyboardInterrupt: + print " exiting..." diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/peci-hwmon-test.bb b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/peci-hwmon-test.bb new file mode 100644 index 000000000..b3b4096a6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/peci-hwmon-test.bb @@ -0,0 +1,20 @@ +SUMMARY = "PECI hwmon test tool" +DESCRIPTION = "command line python tool for testing PECI hwmon" + +SRC_URI = "\ + file://peci-hwmon-test.py \ + " +LICENSE = "CLOSED" + +RDEPENDS_${PN} += "python" + +S = "${WORKDIR}" + +do_compile () { +} + +do_install () { + install -d ${D}/${bindir} + install -m 0755 ${WORKDIR}/peci-hwmon-test.py ${D}/${bindir} +} + |