mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-05 19:31:57 -04:00
Merge branch 'staging/motorola-kernel-modules/MMI-U1UGS34.23-110-23-2' into lineage-22.2
Based on tag 'MMI-U1UGS34.23-110-23-2' of https://github.com/MotorolaMobilityLLC/motorola-kernel-modules Fogos push for Android 14 * staging/motorola-kernel-modules/MMI-U1UGS34.23-110-23-2: (3149 commits) Documentation: Remove slg51000.txt treewide: Remove conflicting Makefiles include: Remove unused synaptics_tcm.h header fs: Remove exfat drivers dlkm: goodix_fod_mmi: fix KE issue dlkm: goodix_fod: support drm panel notification moto_swap: Add reset_bdev to close backing dev for ramboost2.0 add page fault pages to list head to avoid be swapped out soon fogos: TP: open 1st icnl9916c force up config workaround fix long page fault issue Revert "(CR) moto_swap: Add SWP_SYNCHRONOUS_IO cmd for ioctl" fix null pointer at quota_day_store moto_swap: Add SWP_SYNCHRONOUS_IO cmd for ioctl charge: config qg iterm small than FCC moto_swap:fix kernel panic on memcg conf Moto kernel protection module Add macro to close debug function in user build fogo/motorola:Fogo 5g na optimize stowed mode input: goodix_berlin: enable palm cancel for report id Update UWB driver to version 7.4 ... Conflicts: Documentation/devicetree/bindings~HEAD Change-Id: I1e380097e692b2efea451cbcf4dc42d972e6dd5a
This commit is contained in:
commit
af40082e75
2037 changed files with 1289620 additions and 0 deletions
203
AndroidKernelModule.mk
Normal file
203
AndroidKernelModule.mk
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
USE_NEW_DLKM_MK := false
|
||||
|
||||
ifeq ($(call is-board-platform-in-list,taro kalama parrot crow pineapple), true)
|
||||
#Taro and beyond are using a new mechanisim to build kernel modules
|
||||
USE_NEW_DLKM_MK := true
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BOARD_PLATFORM), bengal)
|
||||
ifeq ($(TARGET_KERNEL_VERSION), 5.15)
|
||||
#new bengal uses new mechanisim to build kernel modules
|
||||
USE_NEW_DLKM_MK := true
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(USE_NEW_DLKM_MK), true)
|
||||
ifneq (,$(findstring $(LOCAL_MODULE),$(BOARD_VENDOR_KERNEL_MODULES)))
|
||||
#Taro and beyond are using a new mechanisim to build kernel modules
|
||||
include device/qcom/common/dlkm/Build_external_kernelmodule.mk
|
||||
endif
|
||||
else
|
||||
ifneq (,$(findstring gki,$(KERNEL_DEFCONFIG)))
|
||||
#GKI product should use Qcom latest dlkm compile file
|
||||
include device/qcom/common/dlkm/AndroidKernelModule.mk
|
||||
else
|
||||
DISABLE_THIS_DLKM := $(strip $(TARGET_KERNEL_DLKM_DISABLE))
|
||||
|
||||
ifeq ($(DISABLE_THIS_DLKM),true)
|
||||
ifneq (,$(filter $(LOCAL_MODULE),$(TARGET_KERNEL_DLKM_OVERRIDE)))
|
||||
DISABLE_THIS_DLKM = false
|
||||
else
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(DISABLE_THIS_DLKM),true)
|
||||
$(warning DLKM '$(LOCAL_MODULE)' disabled for target)
|
||||
else
|
||||
|
||||
# Assign external kernel modules to the DLKM class
|
||||
LOCAL_MODULE_CLASS := DLKM
|
||||
|
||||
# Set the default install path to vendor/lib/modules
|
||||
LOCAL_MODULE_PATH := $(strip $(LOCAL_MODULE_PATH))
|
||||
ifeq ($(LOCAL_MODULE_PATH),)
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules
|
||||
endif
|
||||
|
||||
# Set the default Kbuild file path to LOCAL_PATH
|
||||
KBUILD_FILE := $(strip $(KBUILD_FILE))
|
||||
ifeq ($(KBUILD_FILE),)
|
||||
KBUILD_FILE := $(LOCAL_PATH)/Kbuild
|
||||
endif
|
||||
|
||||
# Get rid of any whitespace
|
||||
LOCAL_MODULE_KBUILD_NAME := $(strip $(LOCAL_MODULE_KBUILD_NAME))
|
||||
|
||||
include $(BUILD_SYSTEM)/base_rules.mk
|
||||
|
||||
|
||||
# The kernel build system doesn't support parallel kernel module builds
|
||||
# that share the same output directory. Thus, in order to build multiple
|
||||
# kernel modules that reside in a single directory (and therefore have
|
||||
# the same output directory), there must be just one invocation of the
|
||||
# kernel build system that builds all the modules of a given directory.
|
||||
#
|
||||
# Therefore, all kernel modules must depend on the same, unique target
|
||||
# that invokes the kernel build system and builds all of the modules
|
||||
# for the directory. The $(KBUILD_TARGET) target serves this purpose.
|
||||
# To ensure the value of KBUILD_TARGET is unique, it is essentially set
|
||||
# to the path of the source directory, i.e. LOCAL_PATH.
|
||||
#
|
||||
# Since KBUILD_TARGET is used as a target and a variable name, it should
|
||||
# not contain characters other than letters, numbers, and underscores.
|
||||
KBUILD_TARGET := $(strip \
|
||||
$(subst .,_, \
|
||||
$(subst -,_, \
|
||||
$(subst :,_, \
|
||||
$(subst /,_,$(LOCAL_PATH))))))
|
||||
|
||||
# Intermediate directory where the kernel modules are created
|
||||
# by the kernel build system. Ideally this would be the same
|
||||
# directory as LOCAL_BUILT_MODULE, but because we're using
|
||||
# relative paths for both O= and M=, we don't have much choice
|
||||
KBUILD_OUT_DIR := $(TARGET_OUT_INTERMEDIATES)/$(LOCAL_PATH)
|
||||
|
||||
# Path to the intermediate location where the kernel build
|
||||
# system creates the kernel module.
|
||||
KBUILD_MODULE := $(KBUILD_OUT_DIR)/$(LOCAL_MODULE)
|
||||
|
||||
# Since we only invoke the kernel build system once per directory,
|
||||
# each kernel module must depend on the same target.
|
||||
$(KBUILD_MODULE): kbuild_out := $(KBUILD_OUT_DIR)/$(LOCAL_MODULE_KBUILD_NAME)
|
||||
$(KBUILD_MODULE): $(KBUILD_TARGET)
|
||||
ifneq "$(LOCAL_MODULE_KBUILD_NAME)" ""
|
||||
mv -f $(kbuild_out) $@
|
||||
endif
|
||||
|
||||
|
||||
# To ensure KERNEL_OUT and TARGET_PREBUILT_INT_KERNEL are defined,
|
||||
# kernel/AndroidKernel.mk (kernel_definitions.mk) must be included. While m and regular
|
||||
# make builds will include kernel/AndroidKernel.mk, mm and mmm builds
|
||||
# do not. Therefore, we need to explicitly include kernel/AndroidKernel.mk (kernel_definitions.mk).
|
||||
# It is safe to include it more than once because the entire file is
|
||||
# guarded by "ifeq ($(TARGET_PREBUILT_KERNEL),) ... endif".
|
||||
# If AndroidKernel.mk is not found, fall back to use the kernel_definitions.mk
|
||||
ifneq ($(wildcard $(TARGET_KERNEL_SOURCE)/AndroidKernel.mk),)
|
||||
TARGET_KERNEL_PATH := $(TARGET_KERNEL_SOURCE)/AndroidKernel.mk
|
||||
else
|
||||
TARGET_KERNEL_PATH := device/qcom/kernelscripts/kernel_definitions.mk
|
||||
endif
|
||||
include $(TARGET_KERNEL_PATH)
|
||||
|
||||
# Simply copy the kernel module from where the kernel build system
|
||||
# created it to the location where the Android build system expects it.
|
||||
# If LOCAL_MODULE_DEBUG_ENABLE is set, strip debug symbols. So that,
|
||||
# the final images generated by ABS will have the stripped version of
|
||||
# the modules
|
||||
ifeq ($(TARGET_KERNEL_VERSION),3.18)
|
||||
MODULE_SIGN_FILE := perl ./$(TARGET_KERNEL_SOURCE)/scripts/sign-file
|
||||
MODSECKEY := $(KERNEL_OUT)/signing_key.priv
|
||||
MODPUBKEY := $(KERNEL_OUT)/signing_key.x509
|
||||
else
|
||||
MODULE_SIGN_FILE := $(KERNEL_OUT)/scripts/sign-file
|
||||
MODSECKEY := $(KERNEL_OUT)/certs/signing_key.pem
|
||||
MODPUBKEY := $(KERNEL_OUT)/certs/signing_key.x509
|
||||
endif
|
||||
|
||||
$(LOCAL_BUILT_MODULE): $(KBUILD_MODULE) | $(ACP)
|
||||
ifneq "$(LOCAL_MODULE_DEBUG_ENABLE)" ""
|
||||
mkdir -p $(dir $@)
|
||||
cp $< $<.unstripped
|
||||
$(TARGET_STRIP) --strip-debug $<
|
||||
cp $< $<.stripped
|
||||
endif
|
||||
@sh -c "\
|
||||
KMOD_SIG_ALL=`cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_ALL | cut -d'=' -f2`; \
|
||||
KMOD_SIG_HASH=`cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_HASH | cut -d'=' -f2 | sed 's/\"//g'`; \
|
||||
if [ \"\$$KMOD_SIG_ALL\" = \"y\" ] && [ -n \"\$$KMOD_SIG_HASH\" ]; then \
|
||||
echo \"Signing kernel module: \" `basename $<`; \
|
||||
cp $< $<.unsigned; \
|
||||
$(MODULE_SIGN_FILE) \$$KMOD_SIG_HASH $(MODSECKEY) $(MODPUBKEY) $<; \
|
||||
fi; \
|
||||
"
|
||||
$(transform-prebuilt-to-target)
|
||||
|
||||
# This should really be cleared in build/core/clear-vars.mk, but for
|
||||
# the time being, we need to clear it ourselves
|
||||
LOCAL_MODULE_KBUILD_NAME :=
|
||||
LOCAL_MODULE_DEBUG_ENABLE :=
|
||||
|
||||
# Ensure the kernel module created by the kernel build system, as
|
||||
# well as all the other intermediate files, are removed during a clean.
|
||||
$(cleantarget): PRIVATE_CLEAN_FILES := $(PRIVATE_CLEAN_FILES) $(KBUILD_OUT_DIR)
|
||||
|
||||
|
||||
# Since this file will be included more than once for directories
|
||||
# with more than one kernel module, the shared KBUILD_TARGET rule should
|
||||
# only be defined once to avoid "overriding commands ..." warnings.
|
||||
ifndef $(KBUILD_TARGET)_RULE
|
||||
$(KBUILD_TARGET)_RULE := 1
|
||||
|
||||
# Kernel modules have to be built after:
|
||||
# * the kernel config has been created
|
||||
# * host executables, like scripts/basic/fixdep, have been built
|
||||
# (otherwise parallel invocations of the kernel build system will
|
||||
# fail as they all try to compile these executables at the same time)
|
||||
# * a full kernel build (to make module versioning work)
|
||||
#
|
||||
# For these reasons, kernel modules are dependent on
|
||||
# TARGET_PREBUILT_INT_KERNEL which will ensure all of the above.
|
||||
#
|
||||
# NOTE: Due to a bug in the kernel build system when using a Kbuild file
|
||||
# and relative paths for both O= and M=, the Kbuild file must
|
||||
# be copied to the output directory.
|
||||
#
|
||||
# NOTE: The following paths are equivalent:
|
||||
# $(KBUILD_OUT_DIR)
|
||||
# $(KERNEL_OUT)/../$(LOCAL_PATH)
|
||||
.PHONY: $(KBUILD_TARGET)
|
||||
$(KBUILD_TARGET): local_path := $(LOCAL_PATH)
|
||||
$(KBUILD_TARGET): kbuild_out_dir := $(KBUILD_OUT_DIR)
|
||||
$(KBUILD_TARGET): kbuild_options := $(KBUILD_OPTIONS)
|
||||
ifneq ($(USE_CLANG_FOR_MODULES),)
|
||||
$(KBUILD_TARGET): $(TARGET_PREBUILT_INT_KERNEL)
|
||||
@mkdir -p $(kbuild_out_dir)
|
||||
$(hide) cp -f $(local_path)/Kbuild $(kbuild_out_dir)/Kbuild
|
||||
$(MAKE) -C $(TARGET_KERNEL_SOURCE) M=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(local_path) O=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS) modules $(kbuild_options) ANDROID_BUILD_TOP=$$(pwd) TOP=$$(pwd)
|
||||
else
|
||||
$(KBUILD_TARGET): $(TARGET_PREBUILT_INT_KERNEL)
|
||||
@mkdir -p $(kbuild_out_dir)
|
||||
$(hide) cp -f $(local_path)/Kbuild $(kbuild_out_dir)/Kbuild
|
||||
$(MAKE) -C $(TARGET_KERNEL_SOURCE) M=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(local_path) O=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_CFLAGS) modules $(kbuild_options) TOP=$$(pwd)
|
||||
endif
|
||||
|
||||
# Once the KBUILD_OPTIONS variable has been used for the target
|
||||
# that's specific to the LOCAL_PATH, clear it. If this isn't done,
|
||||
# then every kernel module would need to explicitly set KBUILD_OPTIONS,
|
||||
# or the variable would have to be cleared in 'include $(CLEAR_VARS)'
|
||||
# which would require a change to build/core.
|
||||
KBUILD_OPTIONS :=
|
||||
endif
|
||||
endif
|
||||
endif #(findstring gki,$(KERNEL_DEFCONFIG))
|
||||
endif #(is-board-platform-in-list,taro....)
|
||||
90
Documentation/leds/ledtrig-sol.rst
Normal file
90
Documentation/leds/ledtrig-sol.rst
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
================================
|
||||
Sign-of-life LED trigger
|
||||
================================
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
This is a Sign-of-life LED trigger for QCOM Tri-led Driver.
|
||||
Refer -> kernel_platform/qcom/proprietary/devicetree/bindings/leds/leds-qti-tri-led.txt
|
||||
This trigger will register with LED core framework to individually
|
||||
get activated or deactivated.
|
||||
The trigger will be registered with the power_supply_class to get charging/discharging
|
||||
state and battery capacity.
|
||||
|
||||
Handle power-supply states for Charging/Discharging
|
||||
===================================================
|
||||
|
||||
Set blink/solid Led settings as below
|
||||
|
||||
LED Brighness depend on Device Charging State
|
||||
----------------------------------------------
|
||||
Full while Device Charging
|
||||
Half Brighness while not Charging
|
||||
|
||||
Led Functional Mode Depend on the Battery capacity
|
||||
--------------------------------------------------
|
||||
Battery Level < LOW_CAP_THRESHOLD_DEFAULT
|
||||
Fast Blink "charging_low" led node(s)
|
||||
|
||||
LOW_CAP_THRESHOLD_DEFAULT < Battery Level < BATT_CAP_THRESHOLD_HIGH
|
||||
Slow Blink "charging" led node(s) (If device charging)
|
||||
Enable Solid 50% brightness for "charging" led node(s) (If device not charging)
|
||||
|
||||
BATT_CAP_THRESHOLD_HIGH < Battery Level < BATT_CAP_THRESHOLD_FULL
|
||||
Slow Blink "charging_full" led node(s) (If device charging)
|
||||
Enable Solid 50% brightness for "charging_full" led node(s) (If device not charging)
|
||||
|
||||
Battery Level == BATT_CAP_THRESHOLD_FULL
|
||||
Enable Solid 100% brightness for "charging_full" led node(s)
|
||||
|
||||
Label each node with corresponding colour in device tree
|
||||
======================================================
|
||||
|
||||
Qcom Tri-Led will have settings for each Led (R,G,B).
|
||||
The trigger driver will control with blink or solid pattern via labels nodes like
|
||||
charging_low node: Charging/Discharging and Low battery state
|
||||
charging node: Charging/Discharging state with medium battery
|
||||
charging_full node: Charging/Discharging state while battery near to Full.
|
||||
|
||||
Example of device tree setting
|
||||
=============================
|
||||
|
||||
::
|
||||
|
||||
&pm8550_rgb {
|
||||
red {
|
||||
status = "ok";
|
||||
label = "charging_low";
|
||||
linux,default-trigger = "sign-of-life";
|
||||
};
|
||||
|
||||
green {
|
||||
status = "ok";
|
||||
label = "charging_full";
|
||||
linux,default-trigger = "sign-of-life";
|
||||
};
|
||||
|
||||
blue {
|
||||
status = "ok";
|
||||
label = "charging";
|
||||
linux,default-trigger = "sign-of-life";
|
||||
};
|
||||
};
|
||||
|
||||
List of Methods in Sol trigger driver
|
||||
====================================
|
||||
static int sol_trig_activate(struct led_classdev *led_cdev)
|
||||
{
|
||||
/* Set default sol data register with power_supply notifier */
|
||||
}
|
||||
|
||||
static void sol_trig_deactivate(struct led_classdev *led_cdev)
|
||||
{
|
||||
/* Unregister with power_supply notifier and Free sol data */
|
||||
}
|
||||
|
||||
static int sol_led_update_status(led_name, blink, brightness, sol_data)
|
||||
{
|
||||
/* API to set sol led colour, brightness etc based on battery status */
|
||||
}
|
||||
119
arch/arm64/boot/dts/vendor/bindings/cs40l2x.txt
vendored
Normal file
119
arch/arm64/boot/dts/vendor/bindings/cs40l2x.txt
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
CS40L20 Boosted Haptics Driver
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : One of "cirrus,cs40l20", "cirrus,cs40l25", "cirrus,cs40l25a"
|
||||
or "cirrus,cs40l25b".
|
||||
|
||||
- reg : The I2C slave address of the device.
|
||||
|
||||
- VA-supply, VP-supply : Regulators for the device's VA and VP supplies,
|
||||
respectively. See the following:
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
- cirrus,boost-ind-nanohenry : Boost inductor value, expressed in nH. Valid
|
||||
values include 1000, 1200, 1500 and 2200.
|
||||
|
||||
- cirrus,boost-cap-microfarad : Total equivalent boost capacitance on the VBST
|
||||
and VAMP pins, derated at 11 volts DC. The value must be rounded to the
|
||||
nearest integer and expressed in uF.
|
||||
|
||||
- cirrus,boost-ipk-milliamp : Boost inductor peak current, expressed in mA.
|
||||
Valid values range from 1600 to 4500 (inclusive) in steps of 50.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios : GPIO used for resetting the device.
|
||||
|
||||
- cirrus,refclk-gpio2 : Boolean for configuring the device to expect its
|
||||
32.768-kHz reference clock on the REFCLK/GPIO2 pin. If this property is
|
||||
omitted, the device expects its 32.768-kHz reference clock on the
|
||||
ASP_BCLK/REFCLK pin. This property is ignored for CS40L25A and CS40L25B
|
||||
devices as reference clock selection is configured automatically.
|
||||
|
||||
- cirrus,f0-default : Default LRA resonant frequency (f0), expressed as
|
||||
follows: cirrus,f0-default = f0 (Hz) * 2^14. This value represents the
|
||||
frequency used during playback of PWLE segments specified with frequency
|
||||
equal to f0; it also serves as the unit-specific f0 input to the click
|
||||
compensation algorithm. It can be overwritten at a later time by writing
|
||||
to the f0_stored sysfs control.
|
||||
|
||||
If this value is omitted or specified as zero, the measurement recorded in
|
||||
the f0_measured sysfs control is used. If LRA diagnostics has not been
|
||||
administered and f0_measured is uninitialized, 2621440 (160 Hz) is used.
|
||||
|
||||
- cirrus,f0-min : Minimum LRA resonant frequency (f0) that may be written to
|
||||
the f0_stored sysfs control, expressed using the same numerical format as
|
||||
cirrus,f0-default. If this value is omitted or specified as zero, no lower-
|
||||
bound validation is performed.
|
||||
|
||||
- cirrus,f0-max : Maximum LRA resonant frequency (f0) that may be written to
|
||||
the f0_stored sysfs control, expressed using the same numerical format as
|
||||
cirrus,f0-default. If this value is omitted or specified as zero, no upper-
|
||||
bound validation is performed.
|
||||
|
||||
- cirrus,redc-default : Default LRA series resistance (ReDC), expressed as
|
||||
follows: cirrus,redc-default = ReDC (ohms) / 5.857 * 2^17. This value
|
||||
represents the unit-specific ReDC input to the click compensation algorithm.
|
||||
It can be overwritten at a later time by writing to the redc_stored sysfs
|
||||
control.
|
||||
|
||||
If this value is omitted or specified as zero, the measurement recorded in
|
||||
the redc_measured sysfs control is used. If LRA diagnostics has not been
|
||||
administered and redc_measured is uninitialized, 340787 (15.2 ohms) is used.
|
||||
|
||||
- cirrus,redc-min : Minimum LRA series resistance (ReDC) that may be written
|
||||
to the redc_stored sysfs control, expressed using the same numerical format
|
||||
as cirrus,redc-default. If this value is omitted or specified as zero, no
|
||||
lower-bound validation is performed.
|
||||
|
||||
- cirrus,redc-max : Maximum LRA series resistance (ReDC) that may be written
|
||||
to the redc_stored sysfs control, expressed using the same numerical format
|
||||
as cirrus,redc-default. If this value is omitted or specified as zero, no
|
||||
upper-bound validation is performed.
|
||||
|
||||
- cirrus,gpio1-rise-index : Specifies the wavetable index mapped to GPIO1
|
||||
rising edges. If this value is omitted, specified as zero or exceeds the
|
||||
maximum available index in the wavetable, GPIO1 rising edges are mapped to
|
||||
index 1.
|
||||
|
||||
- cirrus,gpio1-fall-index : Specifies the wavetable index mapped to GPIO1
|
||||
falling edges. If this value is omitted, specified as zero or exceeds the
|
||||
maximum available index in the wavetable, GPIO1 falling edges are mapped to
|
||||
index 2.
|
||||
|
||||
- cirrus,gpio1-fall-timeout : Specifies the number of 48-kHz periods for
|
||||
which the device remains in the active state in search of a GPIO1 falling
|
||||
edge, following a GPIO1 rising edge (the latter of which renders the device
|
||||
active). If a GPIO1 falling edge does not arrive within this timeout, the
|
||||
device automatically returns to the standby state and the subsequent GPIO1
|
||||
falling edge is ignored. If this value is omitted or exceeds the maximum
|
||||
timeout (8388607) then a default of 240000 is assumed (corresponding to
|
||||
240000 / 48000 = 5 seconds). If this value is specified as zero, the timeout
|
||||
is effectively disabled.
|
||||
|
||||
- cirrus,gpio1-mode : Specifies the operating mode of the GPIO1 pin, equal to
|
||||
one of the following.
|
||||
|
||||
0 = enabled by default
|
||||
1 = disabled by default
|
||||
2 = disabled by default, but automatically enabled and disabled upon suspend
|
||||
and resume, respectively
|
||||
|
||||
If this value is omitted or given an invalid value, mode 0 (enabled by
|
||||
default) is assumed. Regardless of mode, the GPIO1 pin can be enabled or
|
||||
disabled at will from user space using the gpio1_enable sysfs control.
|
||||
|
||||
Example:
|
||||
|
||||
cs40l20: cs40l20@43 {
|
||||
compatible = "cirrus,cs40l20";
|
||||
reg = <0x43>;
|
||||
reset-gpios = <&gpio0 54 0>;
|
||||
VA-supply = <&dummy_vreg>;
|
||||
VP-supply = <&dummy_vreg>;
|
||||
cirrus,boost-ind-nanohenry = <1000>;
|
||||
cirrus,boost-cap-microfarad = <4>;
|
||||
cirrus,boost-ipk-milliamp = <4500>;
|
||||
cirrus,refclk-gpio2;
|
||||
};
|
||||
63
arch/arm64/boot/dts/vendor/bindings/drv8424.txt
vendored
Normal file
63
arch/arm64/boot/dts/vendor/bindings/drv8424.txt
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
DRV8424 stepper motor driver from Texas Instruments
|
||||
|
||||
Motor driver is a platform device. Motorola specific HW layout
|
||||
allows for the following controllable GPIOs:
|
||||
|
||||
- MOTOR_POWER_EN
|
||||
- MOTOR_FAULT_INT
|
||||
- MOTOR_STEP
|
||||
- MOTOR_DIR
|
||||
- MOTOR_EN
|
||||
- MOTOR_SLEEP
|
||||
- MOTOR_ACTIVE
|
||||
|
||||
There is no kernel configuration option associated with DRV8424 DLKM
|
||||
|
||||
Required properties:
|
||||
- compatible: only one supported "moto,drv8424"
|
||||
- drv8424-gpio-cells: number of elements allocated for gpio array in the driver
|
||||
- MOTOR_POWER_EN-gpio: gpio enabling power to the motor
|
||||
- MOTOR_FAULT_INT-gpio: gpio used as IRQ to indicate motor fault
|
||||
- MOTOR_STEP-gpio: gpio used as input for pulse generator
|
||||
- MOTOR_DIR-gpio: gpio to control motor direction
|
||||
- MOTOR_EN-gpio: gpio to control drv8424 EN state
|
||||
- MOTOR_SLEEP-gpio: gpio to control drv8424 SLEEP state
|
||||
- MOTOR_ACTIVE-gpio: gpio to control hall effect sensor measurements
|
||||
- pinctrl-names: pinctrl state "drv8424_int_default" required for motor fault IRQ
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-names: list of pre-defined name (currently only "init_state" supported)
|
||||
- power-default-off: keep power off when not in use
|
||||
- support-mode: driver supports MODE0 and MODE1 setup
|
||||
- support-torque: driver supports T0 and T1 setup
|
||||
- no-sensors: ignore sensors data (mostly debug option)
|
||||
|
||||
Note:
|
||||
Only following pinctrl state names are valid!!!
|
||||
"m0_low", "m0_high", "m0_disable",
|
||||
"m1_low", "m1_high", "m1_disable",
|
||||
"t0_low", "t0_high", "t0_disable",
|
||||
"t1_low", "t1_high", "t1_disable",
|
||||
"drv8424_int_default",
|
||||
"drv8424_clk_active",
|
||||
"drv8424_clk_sleep",
|
||||
"init_state"
|
||||
|
||||
Example:
|
||||
|
||||
moto_drv8424_support {
|
||||
compatible = "moto,drv8424";
|
||||
drv8424-gpios-cells = <11>;
|
||||
pinctrl-names = "drv8424_int_default", "init_state";
|
||||
pinctrl-0 = <&drv8424_int_default>;
|
||||
pinctrl-1 = <&drv8424_pwr_en_init &drv8424_step_init &drv8424_dir_init
|
||||
&drv8424_sleep_init &drv8424_en_init &drv8424_step_init &drv8424_active_init>;
|
||||
MOTOR_POWER_EN-gpio = <&tlmm 102>;
|
||||
MOTOR_FAULT_INT-gpio = <&tlmm 112>;
|
||||
MOTOR_STEP-gpio = <&tlmm 182>;
|
||||
MOTOR_DIR-gpio = <&tlmm 183>;
|
||||
MOTOR_SLEEP-gpio = <&tlmm 184>;
|
||||
MOTOR_ACTIVE-gpio = <&tlmm 185>;
|
||||
vdd-supply = <&S10B>;
|
||||
power-default-off;
|
||||
};
|
||||
28
arch/arm64/boot/dts/vendor/bindings/input/misc/ets_fps_mmi/ets_fps.txt
vendored
Normal file
28
arch/arm64/boot/dts/vendor/bindings/input/misc/ets_fps_mmi/ets_fps.txt
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
Egis Technology Inc fingerprint driver
|
||||
et320 is a fps sensor in front of the phone, it connects to mainboard via SPI.
|
||||
et516 is a fps sensor on the back of the phone, it connects to mainboard via SPI.
|
||||
Required properties:
|
||||
- compatible: Should be "egistec,et516" or "egistec,et320"
|
||||
- gpio_irq : GPIO used for interrupt
|
||||
- gpio_rst: GPIO used for ets sensor reset
|
||||
- gpio_ldo1p8_en : GPIO used to enable vdd 1.8v
|
||||
- gpio_ldo3p3_en : GPIO used to enable vcc 3.3v
|
||||
- pinctrl-names,pinctrl-0: the pincontrol setting to configure muxing properly
|
||||
for pins that connect to ets fps sensor.
|
||||
|
||||
Example:
|
||||
ets_fps: ets_fps{
|
||||
compatible = "egistec,et320","egistec,et516";
|
||||
status = "ok";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <128 0x0>;
|
||||
egistec,gpio_irq = <&tlmm 128 0x1>;
|
||||
egistec,gpio_rst = <&tlmm 127 0x2>;
|
||||
egistec,gpio_ldo1p8_en = <&tlmm 132 0x0>;
|
||||
egistec,gpio_ldo3p3_en = <&tlmm 133 0x0>;
|
||||
pinctrl-names = "default", "suspend";
|
||||
pinctrl-0 = <&rst_active &int_active &ldo_en_active &ldo3_en_active>;
|
||||
pinctrl-1 = <&rst_suspend &int_suspend &ldo_en_suspend &ldo3_en_suspend>;
|
||||
};
|
||||
18
arch/arm64/boot/dts/vendor/bindings/input/misc/goodix_fod_mmi/goodix_fod_mmi.txt
vendored
Normal file
18
arch/arm64/boot/dts/vendor/bindings/input/misc/goodix_fod_mmi/goodix_fod_mmi.txt
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Goodix Fingerprint TEE driver
|
||||
|
||||
Required properties:
|
||||
|
||||
-compatible : should be "goodix,fingerprint".
|
||||
-interrupt-parent : gpio interrupt parent declare
|
||||
-fp-gpio-irq : irq gpio declare
|
||||
-fp-gpio-reset : reset gpio declare
|
||||
|
||||
Example:
|
||||
|
||||
goodix_fp {
|
||||
compatible = "goodix,fingerprint";
|
||||
interrupt-parent = <&tlmm>;
|
||||
fp-gpio-irq = <&tlmm 58 0x00>;
|
||||
fp-gpio-reset = <&tlmm 59 0x00>;
|
||||
status = "okay";
|
||||
};
|
||||
28
arch/arm64/boot/dts/vendor/bindings/input/misc/stmvl53l1.txt
vendored
Normal file
28
arch/arm64/boot/dts/vendor/bindings/input/misc/stmvl53l1.txt
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
st,stmvl53l1 laser driver
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "st,stmvl53l1".
|
||||
|
||||
Optional properites:
|
||||
|
||||
|
||||
Example:
|
||||
stmvl53l1@29 {
|
||||
status = "ok";
|
||||
compatible = "st,stmvl53l1";
|
||||
reg = <0x29>;
|
||||
/* L22 is always on for now */
|
||||
/* vdd-supply = <&pm8998_l22>; */
|
||||
gpios = <&pm8998_gpios 15 0>, <&tlmm 79 0>;
|
||||
qcom,gpio-reset = <0>;
|
||||
qcom,gpio-req-tbl-num = <0 1>;
|
||||
qcom,gpio-req-tbl-flags = <0 1>;
|
||||
qcom,gpio-req-tbl-label ="TOF_XSHUTDOWN","TOF_INT";
|
||||
pinctrl-names = "laser_default", "laser_suspend";
|
||||
pinctrl-0 = <&laser_active >;
|
||||
pinctrl-1 = <&laser_suspend >;
|
||||
st,sensorthreshold = <60 65>;
|
||||
st,xtalkval = <19>;
|
||||
};
|
||||
|
||||
37
arch/arm64/boot/dts/vendor/bindings/input/misc/sx933x/sx933x.txt
vendored
Normal file
37
arch/arm64/boot/dts/vendor/bindings/input/misc/sx933x/sx933x.txt
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
sx933x cap touch sensor from semtech
|
||||
|
||||
It is a i2c slave device
|
||||
two specific properties for driver init registers's value
|
||||
|
||||
a).CONFIG_INPUT_SX933x corresponding driver sx933x_sar.c,
|
||||
sx933x is 5 channels driver.
|
||||
|
||||
Required properties:
|
||||
- Semtech,button-flag :flag of used buttons,0~4 bits refers to button 0~4
|
||||
- Semtech,reg-num :num of registers need to be set
|
||||
- Semtech,reg-init :registers's val, format is <reg addr, reg val ...>
|
||||
|
||||
Example:
|
||||
|
||||
sx933x@28 {
|
||||
compatible = "Semtech,sx933x";
|
||||
reg = <0x28>;
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <75 0>;
|
||||
Semtech,nirq-gpio = <&tlmm 75 0x00>;
|
||||
cap_vdd-supply = <&pm660l_l8>;
|
||||
Semtech,reg-num = <12>;
|
||||
Semtech,button-flag = <0x07>; //used button 0~2
|
||||
Semtech,reg-init = <0x8020 0x3F00C0
|
||||
0x8024 0x44F
|
||||
0x8028 0x24928000
|
||||
0x802C 0x44F
|
||||
0x8030 0x24960000
|
||||
0x8034 0x44F
|
||||
0x8038 0x24B20000
|
||||
0x803C 0x44F
|
||||
0x8040 0x25920000
|
||||
0x8044 0x44F
|
||||
0x8048 0x2C920000
|
||||
0x4004 0x67 >;
|
||||
};
|
||||
71
arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.txt
vendored
Normal file
71
arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.txt
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
Synaptics TCM I2C touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
should be "synaptics,tcm-i2c"
|
||||
- reg:
|
||||
i2c slave address of device
|
||||
- interrupt-parent:
|
||||
hardware controller of interrupt signal
|
||||
- interrupts:
|
||||
gpio number and flags of interrupt signal
|
||||
- vdd-supply:
|
||||
digital power source
|
||||
- avdd-supply:
|
||||
analog power source
|
||||
- pinctrl-names:
|
||||
- pinctrl-0:
|
||||
- pinctrl-1:
|
||||
should be defined if using pinctrl framework
|
||||
"pmx_ts_active": active configuration of pins
|
||||
"pmx_ts_suspend": disabled configuration of pins
|
||||
- synaptics,bus-reg-name:
|
||||
name of digital power source regulator
|
||||
- synaptics,pwr-reg-name:
|
||||
name of analog power source regulator
|
||||
- synaptics,irq-gpio:
|
||||
interrupt hardware controller, gpio number, and flags
|
||||
- synaptics,irq-on-state:
|
||||
active state of interrupt signal
|
||||
|
||||
Optional properties:
|
||||
- synaptics,power-gpio:
|
||||
hardware controller and gpio number of power control signal
|
||||
- synaptics,power-delay-ms:
|
||||
delay time in ms after powering on device
|
||||
- synaptics,reset-gpio:
|
||||
hardware controller and gpio number of reset signal
|
||||
- synaptics,reset-delay-ms:
|
||||
delay time in ms after issuing reset to device
|
||||
- synaptics,reset-on-state:
|
||||
active state of reset signal
|
||||
- synaptics,reset-active-ms:
|
||||
active duration in ms of reset signal
|
||||
- synaptics,x-flip:
|
||||
flip x axis
|
||||
- synaptics,y-flip:
|
||||
flip y axis
|
||||
- synaptics,swap-axes:
|
||||
swap x and y axes
|
||||
- synaptics,ubl-i2c-addr:
|
||||
i2c slave address of device in microbootloader mode
|
||||
|
||||
Example:
|
||||
synaptics_tcm@2c {
|
||||
compatible = "synaptics,tcm-i2c";
|
||||
reg = <0x2c>;
|
||||
interrupt-parent = <&msm_gpio>;
|
||||
interrupts = <65 0x2008>;
|
||||
vdd-supply = <&pm8994_lvs2>;
|
||||
avdd-supply = <&pm8994_l22>;
|
||||
pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
|
||||
pinctrl-0 = <&ts_active>;
|
||||
pinctrl-1 = <&ts_suspend>;
|
||||
synaptics,pwr-reg-name = "avdd";
|
||||
synaptics,bus-reg-name = "vdd";
|
||||
synaptics,irq-gpio = <&msm_gpio 65 0x2008>;
|
||||
synaptics,irq-on-state = <0>;
|
||||
synaptics,power-delay-ms = <200>;
|
||||
synaptics,reset-delay-ms = <200>;
|
||||
synaptics,ubl-i2c-addr = <0x2c>;
|
||||
};
|
||||
86
arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.txt
vendored
Normal file
86
arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.txt
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
Synaptics TCM SPI touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
should be "synaptics,tcm-spi"
|
||||
- reg:
|
||||
should be 0
|
||||
- spi-max-frequency:
|
||||
maximum spi clock frequency
|
||||
- interrupt-parent:
|
||||
hardware controller of interrupt signal
|
||||
- interrupts:
|
||||
gpio number and flags of interrupt signal
|
||||
- vdd-supply:
|
||||
digital power source
|
||||
- avdd-supply:
|
||||
analog power source
|
||||
- pinctrl-names:
|
||||
- pinctrl-0:
|
||||
- pinctrl-1:
|
||||
should be defined if using pinctrl framework
|
||||
"pmx_ts_active": active configuration of pins
|
||||
"pmx_ts_suspend": disabled configuration of pins
|
||||
- synaptics,bus-reg-name:
|
||||
name of digital power source regulator
|
||||
- synaptics,pwr-reg-name:
|
||||
name of analog power source regulator
|
||||
- synaptics,irq-gpio:
|
||||
interrupt hardware controller, gpio number, and flags
|
||||
- synaptics,irq-on-state:
|
||||
active state of interrupt signal
|
||||
|
||||
Optional properties:
|
||||
- synaptics,spi-mode:
|
||||
spi mode
|
||||
- synaptics,byte-delay-us:
|
||||
inter-byte delay time in us
|
||||
- synaptics,block-delay-us:
|
||||
inter-block delay time in us
|
||||
- synaptics,power-gpio:
|
||||
hardware controller and gpio number of power control signal
|
||||
- synaptics,power-delay-ms:
|
||||
delay time in ms after powering on device
|
||||
- synaptics,reset-gpio:
|
||||
hardware controller and gpio number of reset signal
|
||||
- synaptics,reset-delay-ms:
|
||||
delay time in ms after issuing reset to device
|
||||
- synaptics,reset-on-state:
|
||||
active state of reset signal
|
||||
- synaptics,reset-active-ms:
|
||||
active duration in ms of reset signal
|
||||
- synaptics,x-flip:
|
||||
flip x axis
|
||||
- synaptics,y-flip:
|
||||
flip y axis
|
||||
- synaptics,swap-axes:
|
||||
swap x and y axes
|
||||
- synaptics,ubl-max-freq:
|
||||
maximum spi clock frequency for microbootloader mode
|
||||
- synaptics,ubl-byte-delay-us:
|
||||
inter-byte delay time in us for microbootloader mode
|
||||
|
||||
Example:
|
||||
synaptics_tcm@0 {
|
||||
compatible = "synaptics,tcm-spi";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
interrupt-parent = <&msm_gpio>;
|
||||
interrupts = <65 0x2008>;
|
||||
vdd-supply = <&pm8994_lvs2>;
|
||||
avdd-supply = <&pm8994_l22>;
|
||||
pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
|
||||
pinctrl-0 = <&ts_active>;
|
||||
pinctrl-1 = <&ts_suspend>;
|
||||
synaptics,bus-reg-name = "vdd";
|
||||
synaptics,pwr-reg-name = "avdd";
|
||||
synaptics,irq-gpio = <&msm_gpio 65 0x2008>;
|
||||
synaptics,irq-on-state = <0>;
|
||||
synaptics,spi-mode = <3>;
|
||||
synaptics,byte-delay-us = <0>;
|
||||
synaptics,block-delay-us = <0>;
|
||||
synaptics,power-delay-ms = <200>;
|
||||
synaptics,reset-delay-ms = <200>;
|
||||
synaptics,ubl-max-freq = <5000000>;
|
||||
synaptics,ubl-byte-delay-us = <20>;
|
||||
};
|
||||
14
arch/arm64/boot/dts/vendor/bindings/sound/tas2560-algo.txt
vendored
Normal file
14
arch/arm64/boot/dts/vendor/bindings/sound/tas2560-algo.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Tas2560 algo driver
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "ti,tas2560-algo"
|
||||
|
||||
- tas2560-port-id : afe port id for the mi2s used for tas2560.
|
||||
|
||||
Example:
|
||||
|
||||
ti,tas2560-algo {
|
||||
compatible = "ti,tas2560-algo";
|
||||
ti,tas2560-port-id = <0x1016>, <0x1017>;
|
||||
};
|
||||
14
drivers/backlight/aw99703/Android.mk
Normal file
14
drivers/backlight/aw99703/Android.mk
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := leds_aw99703.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true)
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/
|
||||
else
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
endif
|
||||
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
5
drivers/backlight/aw99703/Kbuild
Normal file
5
drivers/backlight/aw99703/Kbuild
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m += leds_aw99703.o
|
||||
14
drivers/backlight/aw99703/Makefile
Normal file
14
drivers/backlight/aw99703/Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
all: modules
|
||||
|
||||
modules:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
|
||||
|
||||
%:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
|
||||
rm -rf .tmp_versions
|
||||
2055
drivers/backlight/aw99703/align_convert.h
Normal file
2055
drivers/backlight/aw99703/align_convert.h
Normal file
File diff suppressed because it is too large
Load diff
824
drivers/backlight/aw99703/leds_aw99703.c
Normal file
824
drivers/backlight/aw99703/leds_aw99703.c
Normal file
|
|
@ -0,0 +1,824 @@
|
|||
/*
|
||||
* aw99703.c aw99703 backlight module
|
||||
*
|
||||
* Version: v1.0.3
|
||||
*
|
||||
* Copyright (c) 2019 AWINIC Technology CO., LTD
|
||||
*
|
||||
* Author: Joseph <zhangzetao@awinic.com.cn>
|
||||
*
|
||||
* 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 <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/backlight.h>
|
||||
#include "leds_aw99703.h"
|
||||
#include "align_convert.h"
|
||||
|
||||
#define AW99703_LED_DEV "aw99703-bl"
|
||||
#define AW99703_NAME "aw99703-bl"
|
||||
|
||||
#define AW99703_VERSION "v1.0.3"
|
||||
|
||||
struct aw99703_data *g_aw99703_data;
|
||||
|
||||
static int platform_read_i2c_block(struct i2c_client *client, char *writebuf,
|
||||
int writelen, char *readbuf, int readlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (writelen > 0) {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = writelen,
|
||||
.buf = writebuf,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = readlen,
|
||||
.buf = readbuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 2);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: i2c read error.\n",
|
||||
__func__);
|
||||
} else {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = readlen,
|
||||
.buf = readbuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s:i2c read error.\n", __func__);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw99703_i2c_read(struct i2c_client *client, u8 addr, u8 *val)
|
||||
{
|
||||
return platform_read_i2c_block(client, &addr, 1, val, 1);
|
||||
}
|
||||
|
||||
static int platform_write_i2c_block(struct i2c_client *client,
|
||||
char *writebuf, int writelen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = writelen,
|
||||
.buf = writebuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: i2c write error.\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw99703_i2c_write(struct i2c_client *client, u8 addr, const u8 val)
|
||||
{
|
||||
u8 buf[2] = {0};
|
||||
|
||||
buf[0] = addr;
|
||||
buf[1] = val;
|
||||
|
||||
return platform_write_i2c_block(client, buf, sizeof(buf));
|
||||
}
|
||||
static void aw99703_hwen_pin_ctrl(struct aw99703_data *drvdata, int en)
|
||||
{
|
||||
if (gpio_is_valid(drvdata->hwen_gpio)) {
|
||||
if (en) {
|
||||
pr_info("hwen pin is going to be high!---<%d>\n", en);
|
||||
gpio_set_value(drvdata->hwen_gpio, true);
|
||||
usleep_range(3500, 4000);
|
||||
} else {
|
||||
pr_info("hwen pin is going to be low!---<%d>\n", en);
|
||||
gpio_set_value(drvdata->hwen_gpio, false);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int aw99703_gpio_init(struct aw99703_data *drvdata)
|
||||
{
|
||||
|
||||
int ret;
|
||||
|
||||
if (gpio_is_valid(drvdata->hwen_gpio)) {
|
||||
ret = gpio_request(drvdata->hwen_gpio, "hwen_gpio");
|
||||
if (ret < 0) {
|
||||
pr_err("failed to request gpio\n");
|
||||
return -1;
|
||||
}
|
||||
pr_info("gpio is valid %d!\n",drvdata->hwen_gpio);
|
||||
aw99703_hwen_pin_ctrl(drvdata, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aw99703_i2c_write_bit(struct i2c_client *client,
|
||||
unsigned int reg_addr, unsigned int mask, unsigned char reg_data)
|
||||
{
|
||||
unsigned char reg_val = 0;
|
||||
|
||||
aw99703_i2c_read(client, reg_addr, ®_val);
|
||||
reg_val &= mask;
|
||||
reg_val |= reg_data;
|
||||
aw99703_i2c_write(client, reg_addr, reg_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aw99703_brightness_map(unsigned int level)
|
||||
{
|
||||
/*MAX_LEVEL_256*/
|
||||
if (g_aw99703_data->bl_map == 1) {
|
||||
if (level == 255)
|
||||
return 2047;
|
||||
return level * 8;
|
||||
}
|
||||
/*MAX_LEVEL_1024*/
|
||||
if (g_aw99703_data->bl_map == 2)
|
||||
return level * 2;
|
||||
/*MAX_LEVEL_2048*/
|
||||
if (g_aw99703_data->bl_map == 3)
|
||||
return level;
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
static int aw99703_bl_enable_channel(struct aw99703_data *drvdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (drvdata->channel == 3) {
|
||||
pr_info("%s turn all channel on!\n", __func__);
|
||||
ret = aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_LEDCUR,
|
||||
AW99703_LEDCUR_CHANNEL_MASK,
|
||||
AW99703_LEDCUR_CH3_ENABLE |
|
||||
AW99703_LEDCUR_CH2_ENABLE |
|
||||
AW99703_LEDCUR_CH1_ENABLE);
|
||||
} else if (drvdata->channel == 2) {
|
||||
pr_info("%s turn two channel on!\n", __func__);
|
||||
ret = aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_LEDCUR,
|
||||
AW99703_LEDCUR_CHANNEL_MASK,
|
||||
AW99703_LEDCUR_CH2_ENABLE |
|
||||
AW99703_LEDCUR_CH1_ENABLE);
|
||||
} else if (drvdata->channel == 1) {
|
||||
pr_info("%s turn one channel on!\n", __func__);
|
||||
ret = aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_LEDCUR,
|
||||
AW99703_LEDCUR_CHANNEL_MASK,
|
||||
AW99703_LEDCUR_CH1_ENABLE);
|
||||
} else {
|
||||
pr_info("%s all channels are going to be disabled\n", __func__);
|
||||
ret = aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_LEDCUR,
|
||||
AW99703_LEDCUR_CHANNEL_MASK,
|
||||
0x98);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aw99703_pwm_mode_enable(struct aw99703_data *drvdata)
|
||||
{
|
||||
if (drvdata->pwm_mode) {
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_MODE,
|
||||
AW99703_MODE_PDIS_MASK,
|
||||
AW99703_MODE_PDIS_ENABLE);
|
||||
pr_info("%s pwm_mode is enable\n", __func__);
|
||||
} else {
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_MODE,
|
||||
AW99703_MODE_PDIS_MASK,
|
||||
AW99703_MODE_PDIS_DISABLE);
|
||||
pr_info("%s pwm_mode is disable\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void aw99703_ramp_setting(struct aw99703_data *drvdata)
|
||||
{
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_TURNCFG,
|
||||
AW99703_TURNCFG_ON_TIM_MASK,
|
||||
drvdata->ramp_on_time << 4);
|
||||
pr_info("%s drvdata->ramp_on_time is 0x%x\n",
|
||||
__func__, drvdata->ramp_on_time);
|
||||
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_TURNCFG,
|
||||
AW99703_TURNCFG_OFF_TIM_MASK,
|
||||
drvdata->ramp_off_time);
|
||||
pr_info("%s drvdata->ramp_off_time is 0x%x\n",
|
||||
__func__, drvdata->ramp_off_time);
|
||||
|
||||
}
|
||||
static void aw99703_transition_ramp(struct aw99703_data *drvdata)
|
||||
{
|
||||
|
||||
pr_info("%s enter\n", __func__);
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_TRANCFG,
|
||||
AW99703_TRANCFG_PWM_TIM_MASK,
|
||||
drvdata->pwm_trans_dim);
|
||||
pr_info("%s drvdata->pwm_trans_dim is 0x%x\n", __func__,
|
||||
drvdata->pwm_trans_dim);
|
||||
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_TRANCFG,
|
||||
AW99703_TRANCFG_I2C_TIM_MASK,
|
||||
drvdata->i2c_trans_dim);
|
||||
pr_info("%s drvdata->i2c_trans_dim is 0x%x\n",
|
||||
__func__, drvdata->i2c_trans_dim);
|
||||
|
||||
}
|
||||
|
||||
static int aw99703_backlight_init(struct aw99703_data *drvdata)
|
||||
{
|
||||
pr_info("%s enter.\n", __func__);
|
||||
|
||||
aw99703_pwm_mode_enable(drvdata);
|
||||
|
||||
/*mode:map type*/
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_MODE,
|
||||
AW99703_MODE_MAP_MASK,
|
||||
drvdata->map_type);
|
||||
|
||||
/*default OVPSEL 38V*/
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_BSTCTR1,
|
||||
AW99703_BSTCTR1_OVPSEL_MASK,
|
||||
drvdata->ovp_level);
|
||||
|
||||
/*switch frequency 1000kHz*/
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_BSTCTR1,
|
||||
AW99703_BSTCTR1_SF_MASK,
|
||||
AW99703_BSTCTR1_SF_1000KHZ);
|
||||
|
||||
/*OCP SELECT*/
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_BSTCTR1,
|
||||
AW99703_BSTCTR1_OCPSEL_MASK,
|
||||
drvdata->ocp_level);
|
||||
|
||||
/*BSTCRT2 IDCTSEL*/
|
||||
if (drvdata->bl_reconfig_enable && (AW99703_REG_BSTCTR2 == drvdata->bl_slow_reg)) {
|
||||
pr_info("%s: REG_BSTCTR2 slow set\n", __func__);
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_BSTCTR2,
|
||||
AW99703_BSTCTR2_IDCTSEL_MASK | AW99703_BSTCTR2_EMISEL_MASK,
|
||||
AW99703_BSTCTR2_IDCTSEL_10UH | AW99703_BSTCTR2_EMISEL_SLOW1);
|
||||
} else {
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_BSTCTR2,
|
||||
AW99703_BSTCTR2_IDCTSEL_MASK,
|
||||
AW99703_BSTCTR2_IDCTSEL_10UH);
|
||||
}
|
||||
|
||||
/*Backlight current full scale*/
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_LEDCUR,
|
||||
AW99703_LEDCUR_BLFS_MASK,
|
||||
drvdata->full_scale_led << 3);
|
||||
|
||||
aw99703_bl_enable_channel(drvdata);
|
||||
|
||||
aw99703_ramp_setting(drvdata);
|
||||
aw99703_transition_ramp(drvdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aw99703_backlight_enable(struct aw99703_data *drvdata)
|
||||
{
|
||||
pr_info("%s enter.\n", __func__);
|
||||
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_MODE,
|
||||
AW99703_MODE_WORKMODE_MASK,
|
||||
AW99703_MODE_WORKMODE_BACKLIGHT);
|
||||
|
||||
drvdata->enable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aw99703_set_brightness(struct aw99703_data *drvdata, int brt_val)
|
||||
{
|
||||
pr_info("%s brt_val is %d\n", __func__, brt_val);
|
||||
|
||||
if (drvdata->enable == false) {
|
||||
if(brt_val == 0)
|
||||
return 0;
|
||||
aw99703_backlight_init(drvdata);
|
||||
}
|
||||
|
||||
brt_val = aw99703_brightness_map(brt_val);
|
||||
|
||||
if((0 == drvdata->map_type) && (ALIGN_OLED == drvdata->led_current_align)) {
|
||||
brt_val = align_convert[brt_val];
|
||||
pr_info("%s align convert brt_val is %d\n", __func__, brt_val);
|
||||
}
|
||||
|
||||
if (brt_val > 0) {
|
||||
/*enalbe bl mode*/
|
||||
/* set backlight brt_val */
|
||||
aw99703_i2c_write(drvdata->client,
|
||||
AW99703_REG_LEDLSB,
|
||||
brt_val&0x0007);
|
||||
aw99703_i2c_write(drvdata->client,
|
||||
AW99703_REG_LEDMSB,
|
||||
(brt_val >> 3)&0xff);
|
||||
|
||||
/* backlight enable */
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_MODE,
|
||||
AW99703_MODE_WORKMODE_MASK,
|
||||
AW99703_MODE_WORKMODE_BACKLIGHT);
|
||||
drvdata->enable = true;
|
||||
} else {
|
||||
/* standby mode*/
|
||||
aw99703_i2c_write_bit(drvdata->client,
|
||||
AW99703_REG_MODE,
|
||||
AW99703_MODE_WORKMODE_MASK,
|
||||
AW99703_MODE_WORKMODE_STANDBY);
|
||||
}
|
||||
|
||||
drvdata->brightness = brt_val;
|
||||
|
||||
if (drvdata->brightness == 0)
|
||||
drvdata->enable = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef KERNEL_ABOVE_4_14
|
||||
static int aw99703_bl_get_brightness(struct backlight_device *bl_dev)
|
||||
{
|
||||
return bl_dev->props.brightness;
|
||||
}
|
||||
|
||||
static int aw99703_bl_update_status(struct backlight_device *bl_dev)
|
||||
{
|
||||
struct aw99703_data *drvdata = bl_get_data(bl_dev);
|
||||
int brt;
|
||||
|
||||
if (bl_dev->props.state & BL_CORE_SUSPENDED)
|
||||
bl_dev->props.brightness = 0;
|
||||
|
||||
brt = bl_dev->props.brightness;
|
||||
/*
|
||||
* Brightness register should always be written
|
||||
* not only register based mode but also in PWM mode.
|
||||
*/
|
||||
return aw99703_set_brightness(drvdata, brt);
|
||||
}
|
||||
|
||||
static const struct backlight_ops aw99703_bl_ops = {
|
||||
.update_status = aw99703_bl_update_status,
|
||||
.get_brightness = aw99703_bl_get_brightness,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int aw99703_read_chipid(struct aw99703_data *drvdata)
|
||||
{
|
||||
int ret = -1;
|
||||
u8 value = 0;
|
||||
unsigned char cnt = 0;
|
||||
|
||||
while (cnt < AW_READ_CHIPID_RETRIES) {
|
||||
ret = aw99703_i2c_read(drvdata->client, 0x00, &value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to read reg AW99703_REG_ID: %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
switch (value) {
|
||||
case 0x03:
|
||||
pr_info("%s aw99703 detected\n", __func__);
|
||||
return 0;
|
||||
default:
|
||||
pr_info("%s unsupported device revision (0x%x)\n",
|
||||
__func__, value);
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
|
||||
msleep(AW_READ_CHIPID_RETRY_DELAY);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static void __aw99703_work(struct aw99703_data *led,
|
||||
enum led_brightness value)
|
||||
{
|
||||
mutex_lock(&led->lock);
|
||||
aw99703_set_brightness(led, value);
|
||||
mutex_unlock(&led->lock);
|
||||
}
|
||||
|
||||
static void aw99703_work(struct work_struct *work)
|
||||
{
|
||||
struct aw99703_data *drvdata = container_of(work,
|
||||
struct aw99703_data, work);
|
||||
|
||||
__aw99703_work(drvdata, drvdata->led_dev.brightness);
|
||||
}
|
||||
|
||||
|
||||
static void aw99703_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brt_val)
|
||||
{
|
||||
struct aw99703_data *drvdata;
|
||||
|
||||
drvdata = container_of(led_cdev, struct aw99703_data, led_dev);
|
||||
schedule_work(&drvdata->work);
|
||||
}
|
||||
|
||||
static int aw99703_parse_dt(struct aw99703_data *drvdata)
|
||||
{
|
||||
struct device_node *chosen;
|
||||
int rc = -EINVAL;
|
||||
|
||||
pr_info("%s enter\n", __func__);
|
||||
chosen = of_find_node_by_name(NULL, "chosen");
|
||||
if (chosen) {
|
||||
const char *supplier;
|
||||
char *s, *d;
|
||||
|
||||
rc = of_property_read_string(chosen, "mmi,panel_name", (const char **)&supplier);
|
||||
if (rc) {
|
||||
pr_info("%s: cannot read mmi,panel_name %d\n", __func__, rc);
|
||||
} else {
|
||||
int split_num = 0;
|
||||
/* keep panel & ic info */
|
||||
s = (char *)supplier;
|
||||
d = drvdata->panel_info;
|
||||
while (*s && split_num < 2) {
|
||||
*d++ = *s++;
|
||||
if (*s == '_')
|
||||
split_num++;
|
||||
}
|
||||
|
||||
pr_info("%s: panel_info %s\n", __func__, drvdata->panel_info);
|
||||
}
|
||||
of_node_put(chosen);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
aw99703_get_dt_data(struct device *dev, struct aw99703_data *drvdata)
|
||||
{
|
||||
int rc;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bl_channel, temp;
|
||||
|
||||
drvdata->hwen_gpio = of_get_named_gpio(np, "aw99703,hwen-gpio", 0);
|
||||
pr_info("%s drvdata->hwen_gpio --<%d>\n", __func__, drvdata->hwen_gpio);
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,pwm-mode", &drvdata->pwm_mode);
|
||||
if (rc != 0)
|
||||
pr_err("%s pwm-mode not found\n", __func__);
|
||||
else
|
||||
pr_info("%s pwm_mode=%d\n", __func__, drvdata->pwm_mode);
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,map-type", &drvdata->map_type);
|
||||
if (rc != 0) {
|
||||
drvdata->map_type = AW99703_MODE_MAP_LINEAR;
|
||||
pr_err("%s map-type not found\n", __func__);
|
||||
}
|
||||
else
|
||||
pr_info("%s map-type=%d\n", __func__, drvdata->map_type);
|
||||
|
||||
rc = of_property_read_u32(np, "current-align-type", &drvdata->led_current_align);
|
||||
if (rc != 0) {
|
||||
drvdata->led_current_align = ALIGN_NONE;
|
||||
pr_err("%s current-align-type not found\n", __func__);
|
||||
}
|
||||
else
|
||||
pr_info("%s current-align-type=%d\n", __func__, drvdata->led_current_align);
|
||||
|
||||
drvdata->using_lsb = of_property_read_bool(np, "aw99703,using-lsb");
|
||||
pr_info("%s using_lsb --<%d>\n", __func__, drvdata->using_lsb);
|
||||
|
||||
if (drvdata->using_lsb) {
|
||||
drvdata->max_brightness = 2047;
|
||||
} else {
|
||||
drvdata->max_brightness = 255;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,default-brightness", &drvdata->default_brightness);
|
||||
if (rc != 0) {
|
||||
drvdata->default_brightness = drvdata->max_brightness;
|
||||
pr_err("%s default-brightness not found, set to max %d\n", __func__, drvdata->default_brightness);
|
||||
}
|
||||
else
|
||||
pr_info("%s default-brightness=%d\n", __func__, drvdata->default_brightness);
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,bl-fscal-led", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid backlight full-scale led current!\n");
|
||||
} else {
|
||||
drvdata->full_scale_led = temp;
|
||||
pr_info("%s full-scale led current --<%d mA>\n",
|
||||
__func__, drvdata->full_scale_led);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,turn-on-ramp", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid ramp timing ,turnon!\n");
|
||||
} else {
|
||||
drvdata->ramp_on_time = temp;
|
||||
pr_info("%s ramp on time --<%d ms>\n",
|
||||
__func__, drvdata->ramp_on_time);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,turn-off-ramp", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid ramp timing ,,turnoff!\n");
|
||||
} else {
|
||||
drvdata->ramp_off_time = temp;
|
||||
pr_info("%s ramp off time --<%d ms>\n",
|
||||
__func__, drvdata->ramp_off_time);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,pwm-trans-dim", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid pwm-tarns-dim value!\n");
|
||||
} else {
|
||||
drvdata->pwm_trans_dim = temp;
|
||||
pr_info("%s pwm trnasition dimming --<%d ms>\n",
|
||||
__func__, drvdata->pwm_trans_dim);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,i2c-trans-dim", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid i2c-trans-dim value !\n");
|
||||
} else {
|
||||
drvdata->i2c_trans_dim = temp;
|
||||
pr_info("%s i2c transition dimming --<%d ms>\n",
|
||||
__func__, drvdata->i2c_trans_dim);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,bl-channel", &bl_channel);
|
||||
if (rc) {
|
||||
pr_err("Invalid channel setup\n");
|
||||
} else {
|
||||
drvdata->channel = bl_channel;
|
||||
pr_info("%s bl-channel --<%x>\n", __func__, drvdata->channel);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,bl-map", &drvdata->bl_map);
|
||||
if (rc != 0)
|
||||
pr_err("%s bl_map not found\n", __func__);
|
||||
else
|
||||
pr_info("%s bl_map=%d\n", __func__, drvdata->bl_map);
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,ovp-level", &temp);
|
||||
if (rc) {
|
||||
drvdata->ovp_level = AW99703_BSTCTR1_OVPSEL_38V;
|
||||
pr_err("ovp-level not found, set default %x!\n", drvdata->ovp_level);
|
||||
} else {
|
||||
drvdata->ovp_level = temp<<2;
|
||||
pr_info("%s ovp_level %x\n", __func__, drvdata->ovp_level);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,ocp-level", &temp);
|
||||
if (rc) {
|
||||
drvdata->ocp_level = AW99703_BSTCTR1_OCPSEL_2P7A;
|
||||
pr_err("ocp-level not found, set default %x!\n", drvdata->ocp_level);
|
||||
} else {
|
||||
drvdata->ocp_level = temp;
|
||||
pr_info("%s ocp_level %x\n", __func__, drvdata->ocp_level);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "aw99703,bl-slow-reg", &drvdata->bl_slow_reg);
|
||||
if (!rc) {
|
||||
const char *reconfg_panel;
|
||||
|
||||
pr_info("%s bl_slow_reg=%d\n", __func__, drvdata->bl_slow_reg);
|
||||
rc = of_property_read_string(np, "aw99703,bl-reconfig-panel", (const char **)&reconfg_panel);
|
||||
if (rc) {
|
||||
/* enable for all panels */
|
||||
drvdata->bl_reconfig_enable = true;
|
||||
}
|
||||
else {
|
||||
rc = aw99703_parse_dt(drvdata);
|
||||
if (!rc && strstr(drvdata->panel_info, reconfg_panel)) {
|
||||
/* enable for matched panel */
|
||||
drvdata->bl_reconfig_enable = true;
|
||||
}
|
||||
}
|
||||
pr_info("%s bl_reconfig_enable=%d\n", __func__, drvdata->bl_reconfig_enable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
*
|
||||
* sys group attribute: reg
|
||||
*
|
||||
******************************************************/
|
||||
static ssize_t aw99703_i2c_reg_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct aw99703_data *aw99703 = dev_get_drvdata(dev);
|
||||
|
||||
unsigned int databuf[2] = {0, 0};
|
||||
|
||||
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
|
||||
aw99703_i2c_write(aw99703->client,
|
||||
(unsigned char)databuf[0],
|
||||
(unsigned char)databuf[1]);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t aw99703_i2c_reg_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct aw99703_data *aw99703 = dev_get_drvdata(dev);
|
||||
ssize_t len = 0;
|
||||
unsigned char i = 0;
|
||||
unsigned char reg_val = 0;
|
||||
|
||||
for (i = 0; i < AW99703_REG_MAX; i++) {
|
||||
if (!(aw99703_reg_access[i]®_RD_ACCESS))
|
||||
continue;
|
||||
aw99703_i2c_read(aw99703->client, i, ®_val);
|
||||
len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x\n",
|
||||
i, reg_val);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(reg, 0664, aw99703_i2c_reg_show, aw99703_i2c_reg_store);
|
||||
static struct attribute *aw99703_attributes[] = {
|
||||
&dev_attr_reg.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group aw99703_attribute_group = {
|
||||
.attrs = aw99703_attributes
|
||||
};
|
||||
|
||||
static int aw99703_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct aw99703_data *drvdata;
|
||||
#ifdef KERNEL_ABOVE_4_14
|
||||
struct backlight_device *bl_dev;
|
||||
struct backlight_properties props;
|
||||
#endif
|
||||
int err = 0;
|
||||
pr_err("%s enter! driver version %s\n", __func__, AW99703_VERSION);
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
pr_err("%s : I2C_FUNC_I2C not supported\n", __func__);
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!client->dev.of_node) {
|
||||
pr_err("%s : no device node\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
drvdata = kzalloc(sizeof(struct aw99703_data), GFP_KERNEL);
|
||||
if (drvdata == NULL) {
|
||||
pr_err("%s : kzalloc failed\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
drvdata->client = client;
|
||||
drvdata->adapter = client->adapter;
|
||||
drvdata->addr = client->addr;
|
||||
drvdata->brightness = LED_OFF;
|
||||
drvdata->enable = true;
|
||||
drvdata->led_dev.default_trigger = "bkl-trigger";
|
||||
drvdata->led_dev.name = AW99703_LED_DEV;
|
||||
drvdata->led_dev.brightness_set = aw99703_brightness_set;
|
||||
drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
|
||||
mutex_init(&drvdata->lock);
|
||||
INIT_WORK(&drvdata->work, aw99703_work);
|
||||
aw99703_get_dt_data(&client->dev, drvdata);
|
||||
i2c_set_clientdata(client, drvdata);
|
||||
aw99703_gpio_init(drvdata);
|
||||
|
||||
err = aw99703_read_chipid(drvdata);
|
||||
if (err < 0) {
|
||||
pr_err("%s : ID idenfy failed\n", __func__);
|
||||
goto err_init;
|
||||
}
|
||||
|
||||
err = led_classdev_register(&client->dev, &drvdata->led_dev);
|
||||
if (err < 0) {
|
||||
pr_err("%s : Register led class failed\n", __func__);
|
||||
err = -ENODEV;
|
||||
goto err_init;
|
||||
} else {
|
||||
pr_debug("%s: Register led class successful\n", __func__);
|
||||
}
|
||||
|
||||
#ifdef KERNEL_ABOVE_4_14
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.brightness = MAX_BRIGHTNESS;
|
||||
props.max_brightness = MAX_BRIGHTNESS;
|
||||
bl_dev = backlight_device_register(AW99703_NAME, &client->dev,
|
||||
drvdata, &aw99703_bl_ops, &props);
|
||||
#endif
|
||||
|
||||
g_aw99703_data = drvdata;
|
||||
aw99703_backlight_init(drvdata);
|
||||
aw99703_backlight_enable(drvdata);
|
||||
|
||||
aw99703_set_brightness(drvdata, drvdata->default_brightness);
|
||||
err = sysfs_create_group(&client->dev.kobj, &aw99703_attribute_group);
|
||||
if (err < 0) {
|
||||
dev_info(&client->dev, "%s error creating sysfs attr files\n",
|
||||
__func__);
|
||||
goto err_sysfs;
|
||||
}
|
||||
pr_info("%s exit\n", __func__);
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
err_init:
|
||||
gpio_free(drvdata->hwen_gpio);
|
||||
kfree(drvdata);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int aw99703_remove(struct i2c_client *client)
|
||||
{
|
||||
struct aw99703_data *drvdata = i2c_get_clientdata(client);
|
||||
|
||||
led_classdev_unregister(&drvdata->led_dev);
|
||||
|
||||
kfree(drvdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id aw99703_id[] = {
|
||||
{AW99703_NAME, 0},
|
||||
{}
|
||||
};
|
||||
static struct of_device_id match_table[] = {
|
||||
{.compatible = "awinic,aw99703-bl",},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, aw99703_id);
|
||||
|
||||
static struct i2c_driver aw99703_i2c_driver = {
|
||||
.probe = aw99703_probe,
|
||||
.remove = aw99703_remove,
|
||||
.id_table = aw99703_id,
|
||||
.driver = {
|
||||
.name = AW99703_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = match_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(aw99703_i2c_driver);
|
||||
MODULE_DESCRIPTION("Back Light driver for aw99703");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
263
drivers/backlight/aw99703/leds_aw99703.h
Normal file
263
drivers/backlight/aw99703/leds_aw99703.h
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#ifndef _AW99703_REG_H_
|
||||
#define _AW99703_REG_H_
|
||||
|
||||
/*********************************************************
|
||||
*
|
||||
* kernel version
|
||||
*
|
||||
********************************************************/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#define KERNEL_ABOVE_4_14
|
||||
#endif
|
||||
|
||||
/********************************************
|
||||
* Register List
|
||||
*******************************************/
|
||||
#define AW99703_REG_MAX 0x70
|
||||
#define AW99703_REG_ID 0x00
|
||||
#define AW99703_REG_SFTRST 0x01
|
||||
#define AW99703_REG_MODE 0x02
|
||||
#define AW99703_REG_LEDCUR 0x03
|
||||
#define AW99703_REG_BSTCTR1 0x04
|
||||
#define AW99703_REG_BSTCTR2 0x05
|
||||
#define AW99703_REG_LEDLSB 0x06
|
||||
#define AW99703_REG_LEDMSB 0x07
|
||||
#define AW99703_REG_PWM 0x08
|
||||
#define AW99703_REG_TURNCFG 0x09
|
||||
#define AW99703_REG_TRANCFG 0x0a
|
||||
#define AW99703_REG_FLASH 0x0b
|
||||
#define AW99703_REG_AFHIGH 0x0c
|
||||
#define AW99703_REG_AFLOW 0x0d
|
||||
#define AW99703_REG_FLAGS1 0x0e
|
||||
#define AW99703_REG_FLAGS2 0x0f
|
||||
#define AW99703_REG_FLAGS3 0x11
|
||||
#define AW99703_REG_AUTOZERO 0x21
|
||||
#define AW99703_REG_EMI 0x22
|
||||
#define AW99703_REG_BSTCTR3 0x23
|
||||
#define AW99703_REG_BSTCTR4 0x24
|
||||
#define AW99703_REG_BSTCTR5 0x25
|
||||
#define AW99703_REG_LEDCFG 0x26
|
||||
#define AW99703_REG_DITHER 0x27
|
||||
#define AW99703_REG_PWMMSB 0x28
|
||||
#define AW99703_REG_PWMLSB 0x29
|
||||
#define AW99703_REG_TEST 0x31
|
||||
#define AW99703_REG_FLTDIS 0x33
|
||||
#define AW99703_REG_EFUSE1 0x40
|
||||
#define AW99703_REG_EFUSE2 0x41
|
||||
#define AW99703_REG_EFUSE3 0x42
|
||||
#define AW99703_REG_EFUSE4 0x43
|
||||
#define AW99703_REG_EFUSE5 0x44
|
||||
#define AW99703_REG_EFUSE6 0x45
|
||||
#define AW99703_REG_EFUSE7 0x46
|
||||
#define AW99703_REG_EFUSE8 0x47
|
||||
#define AW99703_REG_EFRUN 0x48
|
||||
#define AW99703_REG_EFMODE 0x49
|
||||
#define AW99703_REG_WPRT1 0x67
|
||||
#define AW99703_REG_WPRT2 0x68
|
||||
#define AW99703_REG_SCANEN 0x69
|
||||
|
||||
|
||||
/********************************************
|
||||
* Register Access
|
||||
*******************************************/
|
||||
#define REG_NONE_ACCESS 0
|
||||
#define REG_RD_ACCESS (1 << 0)
|
||||
#define REG_WR_ACCESS (1 << 1)
|
||||
|
||||
const unsigned char aw99703_reg_access[AW99703_REG_MAX] = {
|
||||
[AW99703_REG_ID] = REG_RD_ACCESS,
|
||||
[AW99703_REG_SFTRST] = REG_WR_ACCESS,
|
||||
[AW99703_REG_MODE] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_LEDCUR] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_BSTCTR1] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_BSTCTR2] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_LEDLSB] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_LEDMSB] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_PWM] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_TURNCFG] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_TRANCFG] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_FLASH] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_AFHIGH] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_AFLOW] = REG_RD_ACCESS|REG_WR_ACCESS,
|
||||
[AW99703_REG_FLAGS1] = REG_RD_ACCESS,
|
||||
[AW99703_REG_FLAGS2] = REG_RD_ACCESS,
|
||||
[AW99703_REG_FLAGS3] = REG_RD_ACCESS,
|
||||
[AW99703_REG_AUTOZERO] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EMI] = REG_RD_ACCESS,
|
||||
[AW99703_REG_BSTCTR3] = REG_RD_ACCESS,
|
||||
[AW99703_REG_BSTCTR4] = REG_RD_ACCESS,
|
||||
[AW99703_REG_BSTCTR5] = REG_RD_ACCESS,
|
||||
[AW99703_REG_LEDCFG] = REG_RD_ACCESS,
|
||||
[AW99703_REG_DITHER] = REG_RD_ACCESS,
|
||||
[AW99703_REG_PWMMSB] = REG_RD_ACCESS,
|
||||
[AW99703_REG_PWMLSB] = REG_RD_ACCESS,
|
||||
[AW99703_REG_TEST] = REG_RD_ACCESS,
|
||||
[AW99703_REG_FLTDIS] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE1] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE2] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE3] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE4] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE5] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE6] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE7] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFUSE8] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFRUN] = REG_RD_ACCESS,
|
||||
[AW99703_REG_EFMODE] = REG_RD_ACCESS,
|
||||
[AW99703_REG_WPRT1] = REG_RD_ACCESS,
|
||||
[AW99703_REG_WPRT2] = REG_RD_ACCESS,
|
||||
[AW99703_REG_SCANEN] = REG_RD_ACCESS,
|
||||
};
|
||||
|
||||
#define MAX_BRIGHTNESS (2047)
|
||||
/******************************************************
|
||||
* Register Detail
|
||||
*****************************************************/
|
||||
/*SFTRST:0x01*/
|
||||
#define AW99703_SFTRST_MASK (~(1<<0))
|
||||
#define AW99703_SFTRST_NOT_RST (0<<0)
|
||||
#define AW99703_SFTRST_RST (1<<0)
|
||||
|
||||
/*MODE:0x02*/
|
||||
#define AW99703_MODE_PDIS_MASK (~(1<<4))
|
||||
#define AW99703_MODE_PDIS_ENABLE (0<<4)
|
||||
#define AW99703_MODE_PDIS_DISABLE (1<<4)
|
||||
#define AW99703_MODE_MAP_MASK (~(1<<2))
|
||||
#define AW99703_MODE_MAP_EXPONENTIAL (0<<2)
|
||||
#define AW99703_MODE_MAP_LINEAR (1<<2)
|
||||
#define AW99703_MODE_WORKMODE_MASK (~(3<<0))
|
||||
#define AW99703_MODE_WORKMODE_STANDBY (0<<0)
|
||||
#define AW99703_MODE_WORKMODE_BACKLIGHT (1<<0)
|
||||
#define AW99703_MODE_WORKMODE_FLASH (2<<0)
|
||||
|
||||
/*MODE:0x03*/
|
||||
#define AW99703_LEDCUR_BLFS_MASK (~(31<<3))
|
||||
#define AW99703_LEDCUR_CHANNEL_MASK (~(7<<0))
|
||||
#define AW99703_LEDCUR_CH3_ENABLE (1<<2)
|
||||
#define AW99703_LEDCUR_CH3_DISABLE (0<<2)
|
||||
#define AW99703_LEDCUR_CH2_ENABLE (1<<1)
|
||||
#define AW99703_LEDCUR_CH2_DISABLE (0<<1)
|
||||
#define AW99703_LEDCUR_CH1_ENABLE (1<<0)
|
||||
#define AW99703_LEDCUR_CH1_DISABLE (0<<0)
|
||||
|
||||
/*BSTCTR1:0x04*/
|
||||
#define AW99703_BSTCTR1_SF_SFT_MASK (~(3<<6))
|
||||
#define AW99703_BSTCTR1_SF_SFT_UP20 (1<<6)
|
||||
#define AW99703_BSTCTR1_SF_SFT_DOWN12 (2<<6)
|
||||
#define AW99703_BSTCTR1_SF_SFT_DOWN24 (3<<6)
|
||||
#define AW99703_BSTCTR1_SF_MASK (~(1<<5))
|
||||
#define AW99703_BSTCTR1_SF_500KHZ (0<<5)
|
||||
#define AW99703_BSTCTR1_SF_1000KHZ (1<<5)
|
||||
#define AW99703_BSTCTR1_OVPSEL_MASK (~(7<<2))
|
||||
#define AW99703_BSTCTR1_OVPSEL_17V (0<<2)
|
||||
#define AW99703_BSTCTR1_OVPSEL_24V (1<<2)
|
||||
#define AW99703_BSTCTR1_OVPSEL_31V (2<<2)
|
||||
#define AW99703_BSTCTR1_OVPSEL_38V (3<<2)
|
||||
#define AW99703_BSTCTR1_OVPSEL_41P5V (4<<2)
|
||||
#define AW99703_BSTCTR1_OCPSEL_MASK (~(3<<0))
|
||||
#define AW99703_BSTCTR1_OCPSEL_0P9A (0<<0)
|
||||
#define AW99703_BSTCTR1_OCPSEL_1P8A (1<<0)
|
||||
#define AW99703_BSTCTR1_OCPSEL_2P7A (2<<0)
|
||||
#define AW99703_BSTCTR1_OCPSEL_3P4A (3<<0)
|
||||
|
||||
/*BSTCTR2:0x05*/
|
||||
#define AW99703_BSTCTR2_AFEN_MASK (~(1<<7))
|
||||
#define AW99703_BSTCTR2_AFEN_ENABLE (1<<7)
|
||||
#define AW99703_BSTCTR2_AFEN_DISABLE (0<<7)
|
||||
#define AW99703_BSTCTR2_IDCTSEL_MASK (~(1<<6))
|
||||
#define AW99703_BSTCTR2_IDCTSEL_4P7UH (0<<6)
|
||||
#define AW99703_BSTCTR2_IDCTSEL_10UH (1<<6)
|
||||
#define AW99703_BSTCTR2_EMISEL_MASK (~(7<<3))
|
||||
#define AW99703_BSTCTR2_EMISEL_TYPICAL (0<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_SLOW1 (1<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_SLOW2 (2<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_SLOW3 (3<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_FAST1 (4<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_FAST2 (5<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_FAST3 (6<<3)
|
||||
#define AW99703_BSTCTR2_EMISEL_FAST4 (7<<3)
|
||||
#define AW99703_BSTCTR2_ADEN_MASK (~(1<<2))
|
||||
#define AW99703_BSTCTR2_ADEN_ENABLE (1<<2)
|
||||
#define AW99703_BSTCTR2_ADEN_DISABLE (0<<2)
|
||||
|
||||
/*PWM:0x08*/
|
||||
#define AW99703_PWM_P_SF_MASK (~(3<<6))
|
||||
#define AW99703_PWM_P_SF_800KHZ (0<<6)
|
||||
#define AW99703_PWM_P_SF_4MKHZ (1<<6)
|
||||
#define AW99703_PWM_P_SF_24MKHZ (2<<6)
|
||||
#define AW99703_PWM_P_ACT_MASK (~(1<<5))
|
||||
#define AW99703_PWM_P_ACT_HIGH (0<<5)
|
||||
#define AW99703_PWM_P_ACT_LOW (1<<5)
|
||||
#define AW99703_PWM_P_HYS_MASK (~(7<<2))
|
||||
#define AW99703_PWM_P_HYS_NONE (0<<2)
|
||||
#define AW99703_PWM_P_HYS_1LSB (1<<2)
|
||||
#define AW99703_PWM_P_HYS_2LSB (2<<2)
|
||||
#define AW99703_PWM_P_HYS_3LSB (3<<2)
|
||||
#define AW99703_PWM_P_HYS_4LSB (4<<2)
|
||||
#define AW99703_PWM_P_HYS_5LSB (5<<2)
|
||||
#define AW99703_PWM_P_HYS_6LSB (6<<2)
|
||||
#define AW99703_PWM_P_HYS_7LSB (7<<2)
|
||||
#define AW99703_PWM_P_FLT_MASK (~(3<<0))
|
||||
#define AW99703_PWM_P_FLT_NONE (0<<0)
|
||||
#define AW99703_PWM_P_FLT_100MS (1<<0)
|
||||
#define AW99703_PWM_P_FLT_150MS (2<<0)
|
||||
#define AW99703_PWM_P_FLT_200MS (3<<0)
|
||||
|
||||
/*TURNCFG:0x09*/
|
||||
#define AW99703_TURNCFG_ON_TIM_MASK (~(15<<4))
|
||||
#define AW99703_TURNCFG_OFF_TIM_MASK (~(15<<0))
|
||||
|
||||
/*TRANCFG:0x0A*/
|
||||
#define AW99703_TRANCFG_PWM_TIM_MASK (~(7<<4))
|
||||
#define AW99703_TRANCFG_I2C_TIM_MASK (~(15<<0))
|
||||
|
||||
#define MAX_BRIGHTNESS (2047)
|
||||
#define AW_READ_CHIPID_RETRIES 5
|
||||
#define AW_READ_CHIPID_RETRY_DELAY 2
|
||||
|
||||
enum backlight_exp_current_align {
|
||||
ALIGN_NONE,
|
||||
ALIGN_OLED
|
||||
};
|
||||
|
||||
struct aw99703_data {
|
||||
struct led_classdev led_dev;
|
||||
struct i2c_client *client;
|
||||
struct device dev;
|
||||
struct i2c_adapter *adapter;
|
||||
unsigned short addr;
|
||||
struct mutex lock;
|
||||
struct work_struct work;
|
||||
enum led_brightness brightness;
|
||||
bool enable;
|
||||
unsigned char pwm_cfg;
|
||||
unsigned char full_scale_current;
|
||||
bool brt_code_enable;
|
||||
unsigned short *brt_code_table;
|
||||
int hwen_gpio;
|
||||
unsigned int pwm_mode;
|
||||
unsigned int map_type;
|
||||
bool using_lsb;
|
||||
bool bl_reconfig_enable;
|
||||
char panel_info[16];
|
||||
unsigned int bl_slow_reg;
|
||||
unsigned int pwm_period;
|
||||
unsigned int full_scale_led;
|
||||
unsigned int ramp_on_time;
|
||||
unsigned int ramp_off_time;
|
||||
unsigned int pwm_trans_dim;
|
||||
unsigned int i2c_trans_dim;
|
||||
unsigned int channel;
|
||||
unsigned int ovp_level;
|
||||
unsigned int ocp_level;
|
||||
unsigned int frequency;
|
||||
unsigned int led_current_align;
|
||||
unsigned int default_brightness;
|
||||
unsigned int max_brightness;
|
||||
unsigned int induct_current;
|
||||
unsigned int flash_current;
|
||||
unsigned int flash_timeout;
|
||||
unsigned int bl_map;
|
||||
struct backlight_device *bl_dev;
|
||||
};
|
||||
|
||||
#endif
|
||||
14
drivers/backlight/ktd3136/Android.mk
Normal file
14
drivers/backlight/ktd3136/Android.mk
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := ktd3136_bl.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true)
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/
|
||||
else
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
endif
|
||||
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
5
drivers/backlight/ktd3136/Kbuild
Normal file
5
drivers/backlight/ktd3136/Kbuild
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m += ktd3136_bl.o
|
||||
10
drivers/backlight/ktd3136/Makefile
Normal file
10
drivers/backlight/ktd3136/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
||||
704
drivers/backlight/ktd3136/ktd3136_bl.c
Normal file
704
drivers/backlight/ktd3136/ktd3136_bl.c
Normal file
|
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* KTD3136_BL Driver
|
||||
*
|
||||
* SiliconMitus KTD3136 Backlight driver chip
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/backlight.h>
|
||||
#if defined(CONFIG_FB)
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/fb.h>
|
||||
#endif
|
||||
|
||||
#include "ktd3136_bl.h"
|
||||
|
||||
#define KTD3136_LED_DEV "ktd3136-BL"
|
||||
#define KTD3136_NAME "ktd3136-bl"
|
||||
#define KTD3136_CHIP_ID 0x18
|
||||
|
||||
static int platform_read_i2c_block(struct i2c_client *client, char *writebuf,
|
||||
int writelen, char *readbuf, int readlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (writelen > 0) {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = writelen,
|
||||
.buf = writebuf,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = readlen,
|
||||
.buf = readbuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 2);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: i2c read error.\n",
|
||||
__func__);
|
||||
} else {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = readlen,
|
||||
.buf = readbuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s:i2c read error.\n", __func__);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ktd3136_read_reg(struct i2c_client *client, u8 addr, u8 *val)
|
||||
{
|
||||
return platform_read_i2c_block(client, &addr, 1, val, 1);
|
||||
}
|
||||
|
||||
static int platform_write_i2c_block(struct i2c_client *client, char *writebuf, int writelen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = writelen,
|
||||
.buf = writebuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: i2c write error.\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ktd3136_write_reg(struct i2c_client *client, u8 addr, const u8 val)
|
||||
{
|
||||
u8 buf[2] = {0};
|
||||
|
||||
buf[0] = addr;
|
||||
buf[1] = val;
|
||||
return platform_write_i2c_block(client, buf, sizeof(buf));
|
||||
}
|
||||
static void ktd3136_hwen_pin_ctrl(struct ktd3136_data *drvdata, int en)
|
||||
{
|
||||
if (gpio_is_valid(drvdata->hwen_gpio)) {
|
||||
if (en) {
|
||||
pr_debug("hwen pin is going to be high!---<%d>\n", en);
|
||||
gpio_set_value(drvdata->hwen_gpio, true);
|
||||
} else {
|
||||
pr_debug("hwen pin is going to be low!---<%d>\n", en);
|
||||
gpio_set_value(drvdata->hwen_gpio, false);
|
||||
}
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int ktd3136_gpio_init(struct ktd3136_data *drvdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("%s enter\n", __func__);
|
||||
if (gpio_is_valid(drvdata->hwen_gpio)) {
|
||||
ret = gpio_request(drvdata->hwen_gpio, "ktd_hwen_gpio");
|
||||
if (ret<0) {
|
||||
pr_err("failed to request gpio\n");
|
||||
return -1;
|
||||
}
|
||||
ret = gpio_direction_output(drvdata->hwen_gpio, 0);
|
||||
pr_debug(" request gpio init\n");
|
||||
if (ret<0) {
|
||||
pr_err("failed to set output");
|
||||
gpio_free(drvdata->hwen_gpio);
|
||||
return ret;
|
||||
}
|
||||
pr_debug("gpio is valid!\n");
|
||||
ktd3136_hwen_pin_ctrl(drvdata, 1);
|
||||
}
|
||||
|
||||
pr_info("%s exit\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
static int ktd3136_masked_write(struct i2c_client *client, int reg, u8 mask, u8 val)
|
||||
{
|
||||
int rc;
|
||||
u8 temp;
|
||||
|
||||
rc = ktd3136_read_reg(client, reg, &temp);
|
||||
if (rc < 0) {
|
||||
pr_err("failed to read reg=0x%x, rc=%d\n", reg, rc);
|
||||
} else {
|
||||
temp &= ~mask;
|
||||
temp |= val & mask;
|
||||
rc = ktd3136_write_reg(client, reg, temp);
|
||||
if (rc<0) {
|
||||
pr_err( "failed to write masked data. reg=%03x, rc=%d\n", reg, rc);
|
||||
}
|
||||
}
|
||||
|
||||
ktd3136_read_reg(client, reg, &temp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ktd3136_bl_enable_channel(struct ktd3136_data *drvdata)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (drvdata->channel == 0) {
|
||||
//default value for mode Register, all channel disabled.
|
||||
pr_debug("all channels are going to be disabled\n");
|
||||
ret = ktd3136_write_reg(drvdata->client, REG_PWM, 0x18);//b0001 1000
|
||||
} else if (drvdata->channel == 3) {
|
||||
pr_debug("turn all channel on!\n");
|
||||
ret = ktd3136_masked_write(drvdata->client, REG_PWM, 0x07, 0x07);
|
||||
} else if (drvdata->channel == 2) {
|
||||
ret = ktd3136_masked_write(drvdata->client, REG_PWM, 0x07, 0x03);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
static void ktd3136_pwm_mode_enable(struct ktd3136_data *drvdata, bool en)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
if (en) {
|
||||
if (drvdata->pwm_mode) {
|
||||
pr_debug("already activated!\n");
|
||||
} else {
|
||||
drvdata->pwm_mode = en;
|
||||
}
|
||||
ktd3136_masked_write(drvdata->client, REG_PWM, 0x80, 0x80);
|
||||
} else {
|
||||
if (drvdata->pwm_mode) {
|
||||
drvdata->pwm_mode = en;
|
||||
}
|
||||
ktd3136_masked_write(drvdata->client, REG_PWM, 0x80, 0x00);
|
||||
}
|
||||
|
||||
ktd3136_read_reg(drvdata->client, REG_PWM, &value);
|
||||
pr_debug("current pwm_mode is --<%x>\n", value);
|
||||
}
|
||||
static int ktd_find_bit(int x)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while ((x = x >> 1))
|
||||
i++;
|
||||
|
||||
return i+1;
|
||||
}
|
||||
|
||||
static void ktd3136_ramp_setting(struct ktd3136_data *drvdata)
|
||||
{
|
||||
unsigned int max_time = 16384;
|
||||
int temp = 0;
|
||||
|
||||
if (drvdata->ramp_on_time == 0) {//512us
|
||||
ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0xf0, 0x00);
|
||||
pr_debug("rampon time is 0 \n");
|
||||
} else if (drvdata->ramp_on_time > max_time) {
|
||||
ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0xf0, 0xf0);
|
||||
pr_debug("rampon time is max \n");
|
||||
} else {
|
||||
temp = ktd_find_bit(drvdata->ramp_on_time);
|
||||
ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0xf0, temp<<4);
|
||||
pr_debug("temp is %d\n", temp);
|
||||
}
|
||||
|
||||
if (drvdata->ramp_off_time == 0) {//512us
|
||||
ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0x0f, 0x00);
|
||||
pr_debug("rampoff time is 0 \n");
|
||||
} else if (drvdata->ramp_off_time > max_time) {
|
||||
ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0x0f, 0x0f);
|
||||
pr_debug("rampoff time is max \n");
|
||||
} else {
|
||||
temp = ktd_find_bit(drvdata->ramp_off_time);
|
||||
ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0x0f, temp);
|
||||
pr_debug("temp is %d\n", temp);
|
||||
}
|
||||
|
||||
}
|
||||
static void ktd3136_transition_ramp(struct ktd3136_data *drvdata)
|
||||
{
|
||||
int reg_i2c, reg_pwm, temp;
|
||||
|
||||
if (drvdata->i2c_trans_dim >= 1024) {
|
||||
reg_i2c = 0xf;
|
||||
} else if (drvdata->i2c_trans_dim < 128) {
|
||||
reg_i2c = 0x0;
|
||||
} else {
|
||||
temp =drvdata->i2c_trans_dim/64;
|
||||
reg_i2c = temp-1;
|
||||
pr_debug("reg_i2c is --<0x%x>\n", reg_i2c);
|
||||
}
|
||||
|
||||
if(drvdata->pwm_trans_dim >= 256){
|
||||
reg_pwm = 0x7;
|
||||
}else if(drvdata->pwm_trans_dim < 4){
|
||||
reg_pwm = 0x0;
|
||||
}else{
|
||||
temp = ktd_find_bit(drvdata->pwm_trans_dim);
|
||||
reg_pwm = temp -2;
|
||||
pr_debug("temp is %d\n", temp);
|
||||
}
|
||||
|
||||
ktd3136_masked_write(drvdata->client, REG_TRANS_RAMP, 0x70, reg_pwm);
|
||||
ktd3136_masked_write(drvdata->client, REG_TRANS_RAMP, 0x0f, reg_i2c);
|
||||
|
||||
}
|
||||
|
||||
static int ktd3136_backlight_init(struct ktd3136_data *drvdata)
|
||||
{
|
||||
int err = 0;
|
||||
u8 value;
|
||||
u8 update_value;
|
||||
pr_info("%s enter.\n", __func__);
|
||||
update_value = (drvdata->ovp_level == 32) ? 0x20 : 0x00;
|
||||
(drvdata->induct_current == 2600) ? update_value |=0x08 : update_value;
|
||||
(drvdata->frequency == 1000) ? update_value |=0x40: update_value;
|
||||
|
||||
ktd3136_write_reg(drvdata->client, REG_CONTROL, update_value | 0x06); /* Linear default*/
|
||||
ktd3136_bl_enable_channel(drvdata);
|
||||
if (drvdata->pwm_mode) {
|
||||
ktd3136_pwm_mode_enable(drvdata, true);
|
||||
} else {
|
||||
ktd3136_pwm_mode_enable(drvdata, false);
|
||||
}
|
||||
ktd3136_ramp_setting(drvdata);
|
||||
ktd3136_transition_ramp(drvdata);
|
||||
ktd3136_read_reg(drvdata->client, REG_CONTROL, &value);
|
||||
pr_debug("read control register -before--<0x%x> -after--<0x%x> \n",
|
||||
update_value, value);
|
||||
|
||||
pr_info("%s exit\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ktd3136_backlight_enable(struct ktd3136_data *drvdata)
|
||||
{
|
||||
int err = 0;
|
||||
pr_info("%s enter.\n", __func__);
|
||||
ktd3136_masked_write(drvdata->client, REG_MODE, 0xf8, drvdata->full_scale_led);
|
||||
drvdata->enable = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ktd3136_set_brightness(struct ktd3136_data *drvdata, int brt_val)
|
||||
{
|
||||
pr_info("%s brt_val is %d\n", __func__, brt_val);
|
||||
|
||||
if (drvdata->enable == false)
|
||||
ktd3136_backlight_init(drvdata);
|
||||
|
||||
if (brt_val>0) {
|
||||
ktd3136_masked_write(drvdata->client, REG_MODE, 0x01, 0x01); //enalbe bl mode
|
||||
} else {
|
||||
ktd3136_masked_write(drvdata->client, REG_MODE, 0x01, 0x00); //disable bl mode
|
||||
}
|
||||
if (drvdata->using_lsb) {
|
||||
ktd3136_masked_write(drvdata->client, REG_RATIO_LSB, 0x07, brt_val);
|
||||
ktd3136_masked_write(drvdata->client, REG_RATIO_MSB, 0xff, brt_val>>3);
|
||||
} else {
|
||||
ktd3136_masked_write(drvdata->client, REG_RATIO_LSB, 0x07, ktd3136_brightness_table_reg4[brt_val]);
|
||||
ktd3136_masked_write(drvdata->client, REG_RATIO_MSB, 0xff, ktd3136_brightness_table_reg5[brt_val]);
|
||||
}
|
||||
|
||||
if (drvdata->enable == false)
|
||||
ktd3136_backlight_enable(drvdata);
|
||||
|
||||
drvdata->brightness = brt_val;
|
||||
|
||||
if (drvdata->brightness == 0)
|
||||
drvdata->enable = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef KERNEL_ABOVE_4_14
|
||||
static int ktd3136_bl_get_brightness(struct backlight_device *bl_dev)
|
||||
{
|
||||
return bl_dev->props.brightness;
|
||||
}
|
||||
|
||||
static int ktd3136_bl_update_status(struct backlight_device *bl_dev)
|
||||
{
|
||||
struct ktd3136_data *drvdata = bl_get_data(bl_dev);
|
||||
int brt;
|
||||
|
||||
if (bl_dev->props.state & BL_CORE_SUSPENDED)
|
||||
bl_dev->props.brightness = 0;
|
||||
|
||||
brt = bl_dev->props.brightness;
|
||||
/*
|
||||
* Brightness register should always be written
|
||||
* not only register based mode but also in PWM mode.
|
||||
*/
|
||||
return ktd3136_set_brightness(drvdata, brt);
|
||||
}
|
||||
|
||||
static const struct backlight_ops ktd3136_bl_ops = {
|
||||
.update_status = ktd3136_bl_update_status,
|
||||
.get_brightness = ktd3136_bl_get_brightness,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int ktd3136_backlight_reset(struct ktd3136_data *drvdata)
|
||||
{
|
||||
int err = 0;
|
||||
ktd3136_masked_write(drvdata->client, REG_SW_RESET, 0x01, 0x01);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ktd3136_check_id(struct ktd3136_data *drvdata)
|
||||
{
|
||||
u8 value=0;
|
||||
int err = 0;
|
||||
ktd3136_read_reg(drvdata->client, 0x00, &value);
|
||||
pr_info("%s: ID check: %x0x\n", __func__, value);
|
||||
if (value != KTD3136_CHIP_ID) {
|
||||
pr_err("%s : ID check err\n", __func__);
|
||||
err = -EINVAL;
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ktd3136_check_status(struct ktd3136_data *drvdata)
|
||||
{
|
||||
u8 value=0;
|
||||
|
||||
ktd3136_read_reg(drvdata->client, REG_STATUS, &value);
|
||||
if (value) {
|
||||
pr_err("status bit has been change! <%x>", value);
|
||||
|
||||
if (value & RESET_CONDITION_BITS) {
|
||||
ktd3136_backlight_reset(drvdata);
|
||||
ktd3136_backlight_init(drvdata);
|
||||
ktd3136_backlight_enable(drvdata);
|
||||
}
|
||||
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FB)
|
||||
static int fb_notifier_callback(struct notifier_block *self,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
int *blank;
|
||||
struct fb_event *evdata = data;
|
||||
struct ktd3136_data *drvdata =
|
||||
container_of(self, struct ktd3136_data, fb_notif);
|
||||
|
||||
/*
|
||||
* FB_EVENT_BLANK(0x09): A hardware display blank change occurred.
|
||||
* FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change
|
||||
* occurred.
|
||||
*/
|
||||
if (evdata && evdata->data && (event == FB_EARLY_EVENT_BLANK)) {
|
||||
blank = evdata->data;
|
||||
if (*blank == FB_BLANK_POWERDOWN)
|
||||
drvdata->enable = false;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __ktd3136_work(struct ktd3136_data *led,
|
||||
enum led_brightness value)
|
||||
{
|
||||
mutex_lock(&led->lock);
|
||||
ktd3136_set_brightness(led, value);
|
||||
mutex_unlock(&led->lock);
|
||||
}
|
||||
|
||||
static void ktd3136_work(struct work_struct *work)
|
||||
{
|
||||
struct ktd3136_data *drvdata = container_of(work,
|
||||
struct ktd3136_data, work);
|
||||
|
||||
__ktd3136_work(drvdata, drvdata->led_dev.brightness);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void ktd3136_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brt_val)
|
||||
{
|
||||
struct ktd3136_data *drvdata;
|
||||
|
||||
drvdata = container_of(led_cdev, struct ktd3136_data, led_dev);
|
||||
|
||||
schedule_work(&drvdata->work);
|
||||
}
|
||||
|
||||
static void ktd3136_get_dt_data(struct device *dev, struct ktd3136_data *drvdata)
|
||||
{
|
||||
int rc;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bl_channel, temp;
|
||||
drvdata->hwen_gpio = of_get_named_gpio(np, "ktd,hwen-gpio", 0);
|
||||
pr_info("%s hwen --<%d>\n", __func__, drvdata->hwen_gpio);
|
||||
|
||||
drvdata->pwm_mode = of_property_read_bool(np,"ktd,pwm-mode");
|
||||
pr_debug("pwmmode --<%d> \n", drvdata->pwm_mode);
|
||||
|
||||
drvdata->using_lsb = of_property_read_bool(np, "ktd,using-lsb");
|
||||
pr_info("%s using_lsb --<%d>\n", __func__, drvdata->using_lsb);
|
||||
|
||||
if (drvdata->using_lsb) {
|
||||
drvdata->default_brightness = 0x7ff;
|
||||
drvdata->max_brightness = 2047;
|
||||
} else {
|
||||
drvdata->default_brightness = 0xff;
|
||||
drvdata->max_brightness = 255;
|
||||
}
|
||||
rc = of_property_read_u32(np, "ktd,pwm-frequency", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid pwm-frequency!\n");
|
||||
} else {
|
||||
drvdata->pwm_period = temp;
|
||||
pr_debug("pwm-frequency --<%d> \n", drvdata->pwm_period);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,bl-fscal-led", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid backlight full-scale led current!\n");
|
||||
} else {
|
||||
drvdata->full_scale_led = temp;
|
||||
pr_debug("full-scale led current --<%d mA> \n", drvdata->full_scale_led);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,turn-on-ramp", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid ramp timing ,,turnon!\n");
|
||||
} else {
|
||||
drvdata->ramp_on_time = temp;
|
||||
pr_debug("ramp on time --<%d ms> \n", drvdata->ramp_on_time);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,turn-off-ramp", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid ramp timing ,,turnoff!\n");
|
||||
} else {
|
||||
drvdata->ramp_off_time = temp;
|
||||
pr_debug("ramp off time --<%d ms> \n", drvdata->ramp_off_time);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,pwm-trans-dim", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid pwm-tarns-dim value!\n");
|
||||
}
|
||||
else {
|
||||
drvdata->pwm_trans_dim = temp;
|
||||
pr_debug("pwm trnasition dimming --<%d ms> \n", drvdata->pwm_trans_dim);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,i2c-trans-dim", &temp);
|
||||
if (rc) {
|
||||
pr_err("Invalid i2c-trans-dim value !\n");
|
||||
} else {
|
||||
drvdata->i2c_trans_dim = temp;
|
||||
pr_debug("i2c transition dimming --<%d ms>\n", drvdata->i2c_trans_dim);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,bl-channel", &bl_channel);
|
||||
if (rc) {
|
||||
pr_err("Invalid channel setup\n");
|
||||
} else {
|
||||
drvdata->channel = bl_channel;
|
||||
pr_debug("bl-channel --<%x> \n", drvdata->channel);
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,ovp-level", &temp);
|
||||
if (!rc) {
|
||||
drvdata->ovp_level = temp;
|
||||
pr_debug("ovp-level --<%d> --temp <%d>\n", drvdata->ovp_level, temp);
|
||||
}else
|
||||
pr_err("Invalid OVP level!\n");
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,switching-frequency", &temp);
|
||||
if (!rc) {
|
||||
drvdata->frequency = temp;
|
||||
pr_debug("switching frequency --<%d> \n", drvdata->frequency);
|
||||
} else {
|
||||
pr_err("Invalid Frequency value!\n");
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,inductor-current", &temp);
|
||||
if (!rc) {
|
||||
drvdata->induct_current = temp;
|
||||
pr_debug("inductor current limit --<%d> \n", drvdata->induct_current);
|
||||
} else
|
||||
pr_err("invalid induct_current limit\n");
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,flash-timeout", &temp);
|
||||
if (!rc) {
|
||||
drvdata->flash_timeout = temp;
|
||||
pr_debug("flash timeout --<%d> \n", drvdata->flash_timeout);
|
||||
} else {
|
||||
pr_err("invalid flash-time value!\n");
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "ktd,flash-current", &temp);
|
||||
if (!rc) {
|
||||
drvdata->flash_current = temp;
|
||||
pr_debug("flash current --<0x%x> \n", drvdata->flash_current);
|
||||
} else {
|
||||
pr_err("invalid flash current value!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int ktd3136_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ktd3136_data *drvdata;
|
||||
#ifdef KERNEL_ABOVE_4_14
|
||||
struct backlight_device *bl_dev;
|
||||
struct backlight_properties props;
|
||||
#endif
|
||||
int err = 0;
|
||||
|
||||
pr_info("%s enter!\n", __func__);
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
pr_err("%s : I2C_FUNC_I2C not supported\n", __func__);
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!client->dev.of_node) {
|
||||
pr_err("%s : no device node\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
drvdata = kzalloc(sizeof(struct ktd3136_data), GFP_KERNEL);
|
||||
if (drvdata == NULL) {
|
||||
pr_err("%s : kzalloc failed\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
drvdata->client = client;
|
||||
drvdata->adapter = client->adapter;
|
||||
#ifdef KTD3136_REG_CONFILCT
|
||||
pr_info("%s: client->addr=0x%x\n", __func__, client->addr);
|
||||
client->addr = KTD3136_REG_REAL;
|
||||
pr_info("%s: confilct, reset client->addr to 0x%x\n", __func__, client->addr);
|
||||
#endif
|
||||
drvdata->addr = client->addr;
|
||||
drvdata->brightness = LED_OFF;
|
||||
drvdata->enable = true;
|
||||
drvdata->led_dev.default_trigger = "bkl-trigger";
|
||||
drvdata->led_dev.name = KTD3136_LED_DEV;
|
||||
drvdata->led_dev.brightness_set = ktd3136_brightness_set;
|
||||
drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
|
||||
ktd3136_get_dt_data(&client->dev, drvdata);
|
||||
i2c_set_clientdata(client, drvdata);
|
||||
err =ktd3136_check_id(drvdata);
|
||||
if (err <0) {
|
||||
pr_err("%s : ID idenfy failed\n", __func__);
|
||||
goto err_init;
|
||||
}
|
||||
|
||||
mutex_init(&drvdata->lock);
|
||||
INIT_WORK(&drvdata->work, ktd3136_work);
|
||||
err = led_classdev_register(&client->dev, &drvdata->led_dev);
|
||||
if (err < 0) {
|
||||
pr_err("%s : Register led class failed\n", __func__);
|
||||
err = -ENODEV;
|
||||
goto err_init;
|
||||
} else {
|
||||
pr_info("%s: Register led class successful\n", __func__);
|
||||
}
|
||||
ktd3136_gpio_init(drvdata);
|
||||
|
||||
#ifdef KERNEL_ABOVE_4_14
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.brightness = MAX_BRIGHTNESS;
|
||||
props.max_brightness = MAX_BRIGHTNESS;
|
||||
bl_dev = backlight_device_register(KTD3136_NAME, &client->dev,
|
||||
drvdata, &ktd3136_bl_ops, &props);
|
||||
#endif
|
||||
ktd3136_backlight_init(drvdata);
|
||||
ktd3136_backlight_enable(drvdata);
|
||||
ktd3136_check_status(drvdata);
|
||||
|
||||
#if defined(CONFIG_FB)
|
||||
drvdata->fb_notif.notifier_call = fb_notifier_callback;
|
||||
err = fb_register_client(&drvdata->fb_notif);
|
||||
if (err)
|
||||
pr_err("%s : Unable to register fb_notifier: %d\n", __func__, err);
|
||||
#endif
|
||||
|
||||
pr_info("%s exit\n", __func__);
|
||||
return 0;
|
||||
|
||||
err_init:
|
||||
pr_info("%s err_init\n", __func__);
|
||||
kfree(drvdata);
|
||||
err_out:
|
||||
pr_info("%s err_out\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ktd3136_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ktd3136_data *drvdata = i2c_get_clientdata(client);
|
||||
|
||||
led_classdev_unregister(&drvdata->led_dev);
|
||||
|
||||
kfree(drvdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ktd3136_id[] = {
|
||||
{KTD3136_NAME, 0},
|
||||
{}
|
||||
};
|
||||
static struct of_device_id match_table[] = {
|
||||
{.compatible = "ktd,ktd3136",}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ktd3136_id);
|
||||
|
||||
static struct i2c_driver ktd3136_i2c_driver = {
|
||||
.probe = ktd3136_probe,
|
||||
.remove = ktd3136_remove,
|
||||
.id_table = ktd3136_id,
|
||||
.driver = {
|
||||
.name = KTD3136_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = match_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(ktd3136_i2c_driver);
|
||||
MODULE_DESCRIPTION("Backlight driver for ktd3136");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
120
drivers/backlight/ktd3136/ktd3136_bl.h
Normal file
120
drivers/backlight/ktd3136/ktd3136_bl.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#ifndef _KTD3136_REG_H_
|
||||
#define _KTD3136_REG_H_
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
/*********************************************************
|
||||
*
|
||||
* kernel version
|
||||
*
|
||||
********************************************************/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#define KERNEL_ABOVE_4_14
|
||||
#endif
|
||||
|
||||
#undef pr_debug
|
||||
#define pr_debug pr_info
|
||||
|
||||
/*********************************************************
|
||||
*
|
||||
* kernel version
|
||||
*
|
||||
********************************************************/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#define KERNEL_ABOVE_4_14
|
||||
#endif
|
||||
|
||||
#define KTD3136_REG_CONFILCT
|
||||
#ifdef KTD3136_REG_CONFILCT
|
||||
#define KTD3136_REG_REAL 0x36
|
||||
#endif
|
||||
|
||||
#define MAX_BRIGHTNESS (2047)
|
||||
#define BANK_NONE 0x00
|
||||
#define REG_DEV_ID 0x00
|
||||
#define REG_SW_RESET 0x01
|
||||
#define REG_MODE 0x02
|
||||
#define REG_CONTROL 0x03
|
||||
#define REG_RATIO_LSB 0x04
|
||||
#define REG_RATIO_MSB 0x05
|
||||
#define REG_PWM 0x06
|
||||
#define REG_RAMP_ON 0x07
|
||||
#define REG_TRANS_RAMP 0x08
|
||||
#define REG_FLASH_SETTING 0x09
|
||||
#define REG_STATUS 0x0A
|
||||
#define BIT_CH3_FAULT BIT(7)
|
||||
#define BIT_CH2_FAULT BIT(6)
|
||||
#define BIT_CH1_FAULT BIT(5)
|
||||
#define BIT_FLASH_TIMEOUT BIT(4)
|
||||
#define BIT_OVP BIT(3)
|
||||
#define BIT_UVLO BIT(2)
|
||||
#define BIT_OCP BIT(1)
|
||||
#define BIT_THERMAL_SHUTDOWN BIT(0)
|
||||
|
||||
#define RESET_CONDITION_BITS (BIT_CH3_FAULT | BIT_CH2_FAULT | BIT_CH1_FAULT | BIT_OVP | BIT_OCP)
|
||||
|
||||
|
||||
int ktd3136_brightness_table_reg4[256] = {0x01,0x02,0x04,0x04,0x07,0x02,0x00,0x06,0x04,0x02,0x03,0x04,0x05,0x06,0x02,0x06,0x02,
|
||||
0x06,0x02,0x06,0x02,0x06,0x02,0x04,0x05,0x06,0x05,0x03,0x00,0x05,0x02,0x06,0x02,0x06,0x02,0x06,0x02,0x06,0x02,0x06,
|
||||
0x02,0x06,0x02,0x06,0x02,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x05,0x07,
|
||||
0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,
|
||||
0x04,0x05,0x06,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x06,0x05,
|
||||
0x04,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x07,0x05,0x03,0x01,0x07,0x05,0x03,0x01,0x07,0x05,0x03,
|
||||
0x01,0x07,0x05,0x03,0x00,0x05,0x02,0x07,0x04,0x01,0x06,0x03,0x00,0x05,0x02,0x07,0x04,0x01,0x06,0x03,0x00,0x05,0x02,
|
||||
0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,
|
||||
0x03,0x07,0x03,0x07,0x03,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x06,0x01,
|
||||
0x04,0x07,0x02,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,
|
||||
0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,
|
||||
0x03,0x05,0x07,0x01,0x03,0x04,0x05,0x06,0x07};
|
||||
|
||||
int ktd3136_brightness_table_reg5[256] = {0x00,0x06,0x0C,0x11,0x15,0x1A,0x1E,0x21,0x25,0x29,0x2C,0x2F,0x32,0x35,0x38,0x3A,0x3D,
|
||||
0x3F,0x42,0x44,0x47,0x49,0x4C,0x4E,0x50,0x52,0x54,0x56,0x58,0x59,0x5B,0x5C,0x5E,0x5F,0x61,0x62,0x64,0x65,0x67,0x68,
|
||||
0x6A,0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74,0x75,0x77,0x78,0x7A,0x7B,0x7C,0x7E,0x7F,0x80,0x82,0x83,0x85,0x86,0x87,0x88,
|
||||
0x8A,0x8B,0x8C,0x8D,0x8F,0x90,0x91,0x92,0x94,0x95,0x96,0x97,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA1,0xA2,0xA3,0xA4,
|
||||
0xA5,0xA6,0xA7,0xA8,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xB9,0xBA,0xBB,
|
||||
0xBC,0xBD,0xBE,0xBF,0xC0,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCC,0xCD,0xCE,
|
||||
0xCF,0xCF,0xD0,0xD1,0xD2,0xD2,0xD3,0xD3,0xD4,0xD5,0xD5,0xD6,0xD7,0xD7,0xD8,0xD8,0xD9,0xDA,0xDA,0xDB,0xDC,0xDC,0xDD,
|
||||
0xDD,0xDE,0xDE,0xDF,0xDF,0xE0,0xE0,0xE1,0xE1,0xE2,0xE2,0xE3,0xE3,0xE4,0xE4,0xE5,0xE5,0xE6,0xE6,0xE7,0xE7,0xE8,0xE8,
|
||||
0xE9,0xE9,0xEA,0xEA,0xEB,0xEB,0xEC,0xEC,0xEC,0xED,0xED,0xEE,0xEE,0xEE,0xEF,0xEF,0xEF,0xF0,0xF0,0xF1,0xF1,0xF1,0xF2,
|
||||
0xF2,0xF2,0xF3,0xF3,0xF3,0xF4,0xF4,0xF4,0xF4,0xF5,0xF5,0xF5,0xF5,0xF6,0xF6,0xF6,0xF6,0xF7,0xF7,0xF7,0xF7,0xF8,0xF8,
|
||||
0xF8,0xF8,0xF9,0xF9,0xF9,0xF9,0xFA,0xFA,0xFA,0xFA,0xFB,0xFB,0xFB,0xFB,0xFC,0xFC,0xFC,0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,
|
||||
0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
|
||||
|
||||
struct ktd3136_data {
|
||||
struct led_classdev led_dev;
|
||||
struct i2c_client *client;
|
||||
struct i2c_adapter *adapter;
|
||||
unsigned short addr;
|
||||
struct mutex lock;
|
||||
struct work_struct work;
|
||||
enum led_brightness brightness;
|
||||
#ifdef CONFIG_FB
|
||||
struct notifier_block fb_notif;
|
||||
#endif
|
||||
bool enable;
|
||||
u8 pwm_cfg;
|
||||
u8 boost_ctl;
|
||||
u8 full_scale_current;
|
||||
bool brt_code_enable;
|
||||
u16 *brt_code_table;
|
||||
int hwen_gpio;
|
||||
bool pwm_mode;
|
||||
bool using_lsb;
|
||||
unsigned int pwm_period;
|
||||
unsigned int full_scale_led;
|
||||
unsigned int ramp_on_time;
|
||||
unsigned int ramp_off_time;
|
||||
unsigned int pwm_trans_dim;
|
||||
unsigned int i2c_trans_dim;
|
||||
unsigned int channel;
|
||||
unsigned int ovp_level;
|
||||
unsigned int frequency;
|
||||
unsigned int default_brightness;
|
||||
unsigned int max_brightness;
|
||||
unsigned int induct_current;
|
||||
unsigned int flash_current;
|
||||
unsigned int flash_timeout;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
14
drivers/backlight/lm3697/Android.mk
Normal file
14
drivers/backlight/lm3697/Android.mk
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := leds_lm3697.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true)
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/
|
||||
else
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
endif
|
||||
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
8
drivers/backlight/lm3697/Kbuild
Normal file
8
drivers/backlight/lm3697/Kbuild
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m := leds_lm3697.o
|
||||
leds_lm3697-objs += ti_lm3697.o
|
||||
leds_lm3697-objs += ti_lm3697_backlight.o
|
||||
leds_lm3697-objs += ti_lm3697_backlight_data.o
|
||||
71
drivers/backlight/lm3697/ti-lmu.h
Normal file
71
drivers/backlight/lm3697/ti-lmu.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* TI LMU (Lighting Management Unit) Devices
|
||||
*
|
||||
* Copyright 2017 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MFD_TI_LMU_H__
|
||||
#define __MFD_TI_LMU_H__
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Notifier event */
|
||||
#define LMU_EVENT_MONITOR_DONE 0x01
|
||||
|
||||
enum ti_lmu_id {
|
||||
LM3697,
|
||||
LMU_MAX_ID,
|
||||
};
|
||||
|
||||
enum ti_lmu_max_current {
|
||||
LMU_IMAX_5mA,
|
||||
LMU_IMAX_6mA,
|
||||
LMU_IMAX_7mA = 0x03,
|
||||
LMU_IMAX_8mA,
|
||||
LMU_IMAX_9mA,
|
||||
LMU_IMAX_10mA = 0x07,
|
||||
LMU_IMAX_11mA,
|
||||
LMU_IMAX_12mA,
|
||||
LMU_IMAX_13mA,
|
||||
LMU_IMAX_14mA,
|
||||
LMU_IMAX_15mA = 0x0D,
|
||||
LMU_IMAX_16mA,
|
||||
LMU_IMAX_17mA,
|
||||
LMU_IMAX_18mA,
|
||||
LMU_IMAX_19mA,
|
||||
LMU_IMAX_20mA = 0x13,
|
||||
LMU_IMAX_21mA,
|
||||
LMU_IMAX_22mA,
|
||||
LMU_IMAX_23mA = 0x17,
|
||||
LMU_IMAX_24mA,
|
||||
LMU_IMAX_25mA,
|
||||
LMU_IMAX_26mA,
|
||||
LMU_IMAX_27mA = 0x1C,
|
||||
LMU_IMAX_28mA,
|
||||
LMU_IMAX_29mA,
|
||||
LMU_IMAX_30mA,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_lmu
|
||||
*
|
||||
* @dev: Parent device pointer
|
||||
* @regmap: Used for i2c communcation on accessing registers
|
||||
* @en_gpio: GPIO for HWEN pin [Optional]
|
||||
* @notifier: Notifier for reporting hwmon event
|
||||
*/
|
||||
struct ti_lmu {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
int en_gpio;
|
||||
struct blocking_notifier_head notifier;
|
||||
};
|
||||
#endif
|
||||
183
drivers/backlight/lm3697/ti_lm3697.c
Normal file
183
drivers/backlight/lm3697/ti_lm3697.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* TI LMU (Lighting Management Unit) Core Driver
|
||||
*
|
||||
* Copyright 2017 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include "ti_lm3697.h"
|
||||
#include "ti_lm3697_regulator.h"
|
||||
#include "ti-lmu.h"
|
||||
|
||||
struct ti_lmu_data {
|
||||
struct mfd_cell *cells;
|
||||
int num_cells;
|
||||
unsigned int max_register;
|
||||
};
|
||||
|
||||
#define LM3697_I2C_ADDR 0x36
|
||||
|
||||
static int ti_lmu_enable_hw(struct ti_lmu *lmu, enum ti_lmu_id id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (gpio_is_valid(lmu->en_gpio)) {
|
||||
ret = devm_gpio_request_one(lmu->dev, lmu->en_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "lmu_hwen");
|
||||
if (ret) {
|
||||
dev_err(lmu->dev, "Can not request enable GPIO: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delay about 1ms after HW enable pin control */
|
||||
usleep_range(1000, 1500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ti_lmu_disable_hw(struct ti_lmu *lmu)
|
||||
{
|
||||
if (gpio_is_valid(lmu->en_gpio))
|
||||
gpio_set_value(lmu->en_gpio, 0);
|
||||
}
|
||||
|
||||
|
||||
static struct mfd_cell lm3697_devices[] = {
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3697,
|
||||
.of_compatible = "ti,lm3697-backlight",
|
||||
},
|
||||
/* Monitoring driver for open/short circuit detection */
|
||||
{
|
||||
.name = "ti-lmu-fault-monitor",
|
||||
.id = LM3697,
|
||||
.of_compatible = "ti,lm3697-fault-monitor",
|
||||
},
|
||||
};
|
||||
|
||||
#define TI_LMU_DATA(chip, max_reg) \
|
||||
static const struct ti_lmu_data chip##_data = \
|
||||
{ \
|
||||
.cells = chip##_devices, \
|
||||
.num_cells = ARRAY_SIZE(chip##_devices),\
|
||||
.max_register = max_reg, \
|
||||
} \
|
||||
|
||||
TI_LMU_DATA(lm3697, LM3697_MAX_REG);
|
||||
|
||||
static const struct of_device_id ti_lmu_of_match[] = {
|
||||
{ .compatible = "ti,lm3697", .data = &lm3697_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_lmu_of_match);
|
||||
|
||||
static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &cl->dev;
|
||||
const struct of_device_id *match;
|
||||
const struct ti_lmu_data *data;
|
||||
struct regmap_config regmap_cfg;
|
||||
struct ti_lmu *lmu;
|
||||
int ret;
|
||||
|
||||
if (cl->addr != LM3697_I2C_ADDR)
|
||||
cl->addr = LM3697_I2C_ADDR;
|
||||
|
||||
match = of_match_device(ti_lmu_of_match, dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
/*
|
||||
* Get device specific data from of_match table.
|
||||
* This data is defined by using TI_LMU_DATA() macro.
|
||||
*/
|
||||
data = (struct ti_lmu_data *)match->data;
|
||||
|
||||
lmu = devm_kzalloc(dev, sizeof(*lmu), GFP_KERNEL);
|
||||
if (!lmu)
|
||||
return -ENOMEM;
|
||||
|
||||
lmu->dev = &cl->dev;
|
||||
|
||||
/* Setup regmap */
|
||||
memset(®map_cfg, 0, sizeof(struct regmap_config));
|
||||
regmap_cfg.reg_bits = 8;
|
||||
regmap_cfg.val_bits = 8;
|
||||
regmap_cfg.name = id->name;
|
||||
regmap_cfg.max_register = data->max_register;
|
||||
|
||||
lmu->regmap = devm_regmap_init_i2c(cl, ®map_cfg);
|
||||
if (IS_ERR(lmu->regmap))
|
||||
return PTR_ERR(lmu->regmap);
|
||||
|
||||
/* HW enable pin control and additional power up sequence if required */
|
||||
lmu->en_gpio = of_get_named_gpio(dev->of_node, "enable-gpios", 0);
|
||||
ret = ti_lmu_enable_hw(lmu, id->driver_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Fault circuit(open/short) can be detected by ti-lmu-fault-monitor.
|
||||
* After fault detection is done, some devices should re-initialize
|
||||
* configuration. The notifier enables such kind of handling.
|
||||
*/
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&lmu->notifier);
|
||||
|
||||
i2c_set_clientdata(cl, lmu);
|
||||
|
||||
mfd_add_devices(lmu->dev, 0, data->cells,
|
||||
data->num_cells, NULL, 0, NULL);
|
||||
|
||||
ti_lmu_backlight_device_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_lmu_remove(struct i2c_client *cl)
|
||||
{
|
||||
struct ti_lmu *lmu = i2c_get_clientdata(cl);
|
||||
|
||||
ti_lmu_disable_hw(lmu);
|
||||
mfd_remove_devices(lmu->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ti_lmu_ids[] = {
|
||||
{ "lm3697", LM3697 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ti_lmu_ids);
|
||||
|
||||
static struct i2c_driver ti_lmu_driver = {
|
||||
.probe = ti_lmu_probe,
|
||||
.remove = ti_lmu_remove,
|
||||
.driver = {
|
||||
.name = "ti-lmu",
|
||||
.of_match_table = ti_lmu_of_match,
|
||||
},
|
||||
.id_table = ti_lmu_ids,
|
||||
};
|
||||
|
||||
module_i2c_driver(ti_lmu_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TI LMU MFD Core Driver");
|
||||
MODULE_AUTHOR("Milo Kim");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
252
drivers/backlight/lm3697/ti_lm3697.h
Normal file
252
drivers/backlight/lm3697/ti_lm3697.h
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* TI LMU (Lighting Management Unit) Backlight Common Driver
|
||||
*
|
||||
* Copyright 2016 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __TI_LMU_BACKLIGHT_H__
|
||||
#define __TI_LMU_BACKLIGHT_H__
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include "ti_lm3697_regulator.h"
|
||||
#include "ti-lmu.h"
|
||||
/**
|
||||
* LMU backlight register data
|
||||
* value[23:16] | mask[15:8] | address[7:0]
|
||||
*/
|
||||
#define LMU_BL_REG(addr, mask, value) \
|
||||
((value << 16) | (mask << 8) | addr)
|
||||
|
||||
#define LMU_BL_GET_ADDR(x) (x & 0xFF)
|
||||
#define LMU_BL_GET_MASK(x) ((x >> 8) & 0xFF)
|
||||
#define LMU_BL_GET_VAL(x) ((x >> 16) & 0xFF)
|
||||
|
||||
#define LM3697_INIT_RAMP_SELECT \
|
||||
LMU_BL_REG(LM3697_REG_RAMP_CONF, LM3697_RAMP_MASK, LM3697_RAMP_EACH)
|
||||
#define LM3697_CHANNEL_1 \
|
||||
LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED1_CFG_MASK, \
|
||||
LM3697_HVLED1_CFG_SHIFT)
|
||||
#define LM3697_CHANNEL_2 \
|
||||
LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED2_CFG_MASK, \
|
||||
LM3697_HVLED2_CFG_SHIFT)
|
||||
#define LM3697_CHANNEL_3 \
|
||||
LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED3_CFG_MASK, \
|
||||
LM3697_HVLED3_CFG_SHIFT)
|
||||
#define LM3697_MODE_PWM_A \
|
||||
LMU_BL_REG(LM3697_REG_PWM_CFG, LM3697_PWM_A_MASK, LM3697_PWM_A_MASK)
|
||||
#define LM3697_MODE_PWM_B \
|
||||
LMU_BL_REG(LM3697_REG_PWM_CFG, LM3697_PWM_B_MASK, LM3697_PWM_B_MASK)
|
||||
#define LM3697_RAMPUP \
|
||||
LMU_BL_REG(LM3697_REG_BL0_RAMP, LM3697_RAMPUP_MASK, LM3697_RAMPUP_SHIFT)
|
||||
#define LM3697_RAMPDN \
|
||||
LMU_BL_REG(LM3697_REG_BL0_RAMP, LM3697_RAMPDN_MASK, LM3697_RAMPDN_SHIFT)
|
||||
|
||||
#define LM3697_MAX_CHANNELS 3
|
||||
|
||||
#define MAX_BRIGHTNESS_8BIT 255
|
||||
#define MAX_BRIGHTNESS_11BIT 2047
|
||||
|
||||
enum ti_lmu_bl_ctrl_mode {
|
||||
BL_REGISTER_BASED,
|
||||
BL_PWM_BASED,
|
||||
};
|
||||
|
||||
enum ti_lmu_bl_pwm_action {
|
||||
/* Update PWM duty, no brightness register update is required */
|
||||
UPDATE_PWM_ONLY,
|
||||
/* Update not only duty but also brightness register */
|
||||
UPDATE_PWM_AND_BRT_REGISTER,
|
||||
/* Update max value in brightness registers */
|
||||
UPDATE_MAX_BRT,
|
||||
};
|
||||
|
||||
enum ti_lmu_bl_ramp_mode {
|
||||
BL_RAMP_UP,
|
||||
BL_RAMP_DOWN,
|
||||
};
|
||||
|
||||
struct ti_lmu_bl;
|
||||
struct ti_lmu_bl_chip;
|
||||
|
||||
/**
|
||||
* struct ti_lmu_bl_reg
|
||||
*
|
||||
* @init: Device initialization registers
|
||||
* @num_init: Numbers of initialization registers
|
||||
* @channel: Backlight channel configuration registers
|
||||
* @mode: Brightness control mode registers
|
||||
* @ramp: Ramp registers for lighting effect
|
||||
* @ramp_reg_offset: Ramp register offset.
|
||||
* Only used for multiple ramp registers.
|
||||
* @enable: Enable control register address
|
||||
* @enable_offset: Enable control register bit offset
|
||||
* @enable_usec: Delay time for updating enable register.
|
||||
* Unit is microsecond.
|
||||
* @brightness_msb: Brightness MSB(Upper 8 bits) registers.
|
||||
* Concatenated with LSB in 11 bit dimming mode.
|
||||
* In 8 bit dimming, only MSB is used.
|
||||
* @brightness_lsb: Brightness LSB(Lower 3 bits) registers.
|
||||
* Only valid in 11 bit dimming mode.
|
||||
*/
|
||||
struct ti_lmu_bl_reg {
|
||||
u32 *init;
|
||||
int num_init;
|
||||
u32 *channel;
|
||||
u32 *mode;
|
||||
u32 *ramp;
|
||||
int ramp_reg_offset;
|
||||
u8 *enable;
|
||||
u8 enable_offset;
|
||||
unsigned long enable_usec;
|
||||
u8 *brightness_msb;
|
||||
u8 *brightness_lsb;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_lmu_bl_cfg
|
||||
*
|
||||
* @reginfo: Device register configuration
|
||||
* @num_channels: Number of backlight channels
|
||||
* @single_bank_used: [Optional] Set true if one bank controls multiple channels.
|
||||
* Only used for LM36274.
|
||||
* @max_brightness: Max brightness value of backlight device
|
||||
* @pwm_action: How to control brightness registers in PWM mode
|
||||
* @ramp_table: [Optional] Ramp time table for lighting effect.
|
||||
* It's used for searching approximate register index.
|
||||
* @size_ramp: [Optional] Size of ramp table
|
||||
* @fault_monitor_used: [Optional] Set true if the device needs to handle
|
||||
* LMU fault monitor event.
|
||||
*
|
||||
* This structure is used for device specific data configuration.
|
||||
*/
|
||||
struct ti_lmu_bl_cfg {
|
||||
const struct ti_lmu_bl_reg *reginfo;
|
||||
int num_channels;
|
||||
bool single_bank_used;
|
||||
int max_brightness;
|
||||
enum ti_lmu_bl_pwm_action pwm_action;
|
||||
int *ramp_table;
|
||||
int size_ramp;
|
||||
bool fault_monitor_used;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_lmu_bl_chip
|
||||
*
|
||||
* @dev: Parent device pointer
|
||||
* @lmu: LMU structure.
|
||||
* Used for register R/W access and notification.
|
||||
* @cfg: Device configuration data
|
||||
* @lmu_bl: Multiple backlight channels
|
||||
* @num_backlights: Number of backlight channels
|
||||
* @nb: Notifier block for handling LMU fault monitor event
|
||||
*
|
||||
* One backlight chip can have multiple backlight channels, 'ti_lmu_bl'.
|
||||
*/
|
||||
struct ti_lmu_bl_chip {
|
||||
struct device *dev;
|
||||
struct ti_lmu *lmu;
|
||||
const struct ti_lmu_bl_cfg *cfg;
|
||||
struct ti_lmu_bl *lmu_bl;
|
||||
int num_backlights;
|
||||
struct notifier_block nb;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_lmu_bl
|
||||
*
|
||||
* @chip: Pointer to parent backlight device
|
||||
* @bl_dev: Backlight subsystem device structure
|
||||
* @bank_id: Backlight bank ID
|
||||
* @name: Backlight channel name
|
||||
* @mode: Backlight control mode
|
||||
* @led_sources: Backlight output channel configuration.
|
||||
* Bit mask is set on parsing DT.
|
||||
* @default_brightness: [Optional] Initial brightness value
|
||||
* @ramp_up_msec: [Optional] Ramp up time
|
||||
* @ramp_down_msec: [Optional] Ramp down time
|
||||
* @pwm_period: [Optional] PWM period
|
||||
* @pwm: [Optional] PWM subsystem structure
|
||||
*
|
||||
* Each backlight device has its own channel configuration.
|
||||
* For chip control, parent chip data structure is used.
|
||||
*/
|
||||
struct ti_lmu_bl {
|
||||
struct ti_lmu_bl_chip *chip;
|
||||
struct backlight_device *bl_dev;
|
||||
|
||||
int bank_id;
|
||||
const char *name;
|
||||
enum ti_lmu_bl_ctrl_mode mode;
|
||||
unsigned long led_sources;
|
||||
|
||||
unsigned int default_brightness;
|
||||
|
||||
/* Used for lighting effect */
|
||||
unsigned int ramp_up_msec;
|
||||
unsigned int ramp_down_msec;
|
||||
|
||||
/* Only valid in PWM mode */
|
||||
unsigned int pwm_period;
|
||||
|
||||
/* current mode */
|
||||
unsigned int led_current_mode;
|
||||
|
||||
/* Boost OVP */
|
||||
unsigned int led_boost_ovp;
|
||||
/* Boost frequency */
|
||||
unsigned int led_boost_freq;
|
||||
|
||||
/* Map type */
|
||||
unsigned int map_type;
|
||||
|
||||
/* Align boost current to AW chip */
|
||||
unsigned int led_current_align;
|
||||
|
||||
struct pwm_device *pwm;
|
||||
};
|
||||
|
||||
enum backlight_hbm_mode {
|
||||
HBM_MODE_UN_SET,
|
||||
HBM_MODE_DEFAULT = 1,
|
||||
HBM_MODE_LEVEL1, //CURRENT = HBM_MODE_DEFAULT*112.5%
|
||||
HBM_MODE_LEVEL2, //CURRENT = HBM_MODE_DEFAULT*125%
|
||||
HBM_MODE_LEVEL3, //CURRENT = HBM_MODE_DEFAULT*107%
|
||||
HBM_MODE_LEVEL4, //CURRENT = HBM_MODE_DEFAULT*111%
|
||||
HBM_MODE_LEVEL_MAX
|
||||
};
|
||||
|
||||
enum backlight_boost_ovp {
|
||||
BOOST_OVP_16V,
|
||||
BOOST_OVP_24V,
|
||||
BOOST_OVP_32V,
|
||||
BOOST_OVP_40V
|
||||
};
|
||||
|
||||
enum backlight_boost_freq {
|
||||
BOOST_FREQ_500K,
|
||||
BOOST_FREQ_1M
|
||||
};
|
||||
|
||||
enum backlight_map_type {
|
||||
EXPONENTIAL_TYPE,
|
||||
LINEAR_TYPE
|
||||
};
|
||||
|
||||
enum backlight_exp_current_align {
|
||||
ALIGN_NONE,
|
||||
ALIGN_AW99703
|
||||
};
|
||||
|
||||
extern struct ti_lmu_bl_cfg lmu_bl_cfg[LMU_MAX_ID];
|
||||
extern int ti_lmu_backlight_device_init(void);
|
||||
extern void ti_lmu_backlight_exit(void);
|
||||
#endif
|
||||
963
drivers/backlight/lm3697/ti_lm3697_backlight.c
Normal file
963
drivers/backlight/lm3697/ti_lm3697_backlight.c
Normal file
|
|
@ -0,0 +1,963 @@
|
|||
/*
|
||||
* TI LMU (Lighting Management Unit) Backlight Driver
|
||||
*
|
||||
* Copyright 2016 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
#include "ti_lm3697.h"
|
||||
#include "ti_lm3697_regulator.h"
|
||||
#include "ti-lmu.h"
|
||||
|
||||
#define NUM_DUAL_CHANNEL 2
|
||||
#define LMU_BACKLIGHT_DUAL_CHANNEL_USED (BIT(0) | BIT(1))
|
||||
#define LMU_BACKLIGHT_11BIT_LSB_MASK (BIT(0) | BIT(1) | BIT(2))
|
||||
#define LMU_BACKLIGHT_11BIT_MSB_SHIFT 3
|
||||
#define DEFAULT_PWM_NAME "lmu-backlight"
|
||||
#define LM3697_NAME "lm3697-bl"
|
||||
|
||||
static struct ti_lmu_bl_chip *bl_chip;
|
||||
|
||||
int translate_value[2048] = {0, 50, 55, 171, 253, 317, 360, 414, 452, 486, 516, 543, 568, 591, 612, 632, 651, 668, 684, 700, 714, 728, 742, 754, 767, 778, 790,\
|
||||
800, 811, 821, 831, 840, 849, 858, 866, 875, 883, 891, 898, 906, 913, 920, 927, 934, 940, 947, 953, 959, 965, 971, 977, 983, 988,\
|
||||
994, 999, 1004, 1009, 1014, 1019, 1024, 1029, 1034, 1038, 1043, 1048, 1052, 1056, 1061, 1065, 1069, 1073, 1077, 1081, 1085,\
|
||||
1089, 1093, 1097, 1101, 1104, 1108, 1111, 1115, 1119, 1122, 1125, 1129, 1132, 1135, 1139, 1142, 1145, 1148, 1151, 1155, 1158,\
|
||||
1161, 1164, 1167, 1170, 1172, 1175, 1178, 1181, 1184, 1187, 1189, 1192, 1195, 1197, 1200, 1203, 1205, 1208, 1210, 1213, 1215,\
|
||||
1218, 1220, 1223, 1225, 1228, 1230, 1232, 1235, 1237, 1239, 1242, 1244, 1246, 1248, 1251, 1253, 1255, 1257, 1259, 1261, 1263,\
|
||||
1266, 1268, 1270, 1272, 1274, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1291, 1293, 1295, 1297, 1299, 1301, 1303, 1305, 1306,\
|
||||
1308, 1310, 1312, 1314, 1315, 1317, 1319, 1321, 1322, 1324, 1326, 1327, 1329, 1331, 1332, 1334, 1336, 1337, 1339, 1341, 1342,\
|
||||
1344, 1345, 1347, 1348, 1350, 1352, 1353, 1355, 1356, 1358, 1359, 1361, 1362, 1364, 1365, 1367, 1368, 1370, 1371, 1372, 1374,\
|
||||
1375, 1377, 1378, 1380, 1381, 1382, 1384, 1385, 1386, 1388, 1389, 1391, 1392, 1393, 1395, 1396, 1397, 1399, 1400, 1401, 1402,\
|
||||
1404, 1405, 1406, 1408, 1409, 1410, 1411, 1413, 1414, 1415, 1416, 1418, 1419, 1420, 1421, 1423, 1424, 1425, 1426, 1427, 1428,\
|
||||
1430, 1431, 1432, 1433, 1434, 1435, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452,\
|
||||
1453, 1454, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474,\
|
||||
1475, 1476, 1477, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, 1494, 1495,\
|
||||
1496, 1497, 1498, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1508, 1509, 1510, 1511, 1512, 1513, 1514,\
|
||||
1515, 1516, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1528, 1529, 1530, 1531, 1532,\
|
||||
1533, 1533, 1534, 1535, 1536, 1537, 1537, 1538, 1539, 1540, 1541, 1541, 1542, 1543, 1544, 1545, 1545, 1546, 1547, 1548, 1549,\
|
||||
1549, 1550, 1551, 1552, 1552, 1553, 1554, 1555, 1555, 1556, 1557, 1558, 1558, 1559, 1560, 1561, 1561, 1562, 1563, 1564, 1564,\
|
||||
1565, 1566, 1567, 1567, 1568, 1569, 1570, 1570, 1571, 1572, 1572, 1573, 1574, 1575, 1575, 1576, 1577, 1577, 1578, 1579, 1579,\
|
||||
1580, 1581, 1582, 1582, 1583, 1584, 1584, 1585, 1586, 1586, 1587, 1588, 1588, 1589, 1590, 1590, 1591, 1592, 1592, 1593, 1594,\
|
||||
1594, 1595, 1596, 1596, 1597, 1598, 1598, 1599, 1600, 1600, 1601, 1602, 1602, 1603, 1604, 1604, 1605, 1606, 1606, 1607, 1607,\
|
||||
1608, 1609, 1609, 1610, 1611, 1611, 1612, 1612, 1613, 1614, 1614, 1615, 1616, 1616, 1617, 1617, 1618, 1619, 1619, 1620, 1620,\
|
||||
1621, 1622, 1622, 1623, 1623, 1624, 1625, 1625, 1626, 1626, 1627, 1628, 1628, 1629, 1629, 1630, 1631, 1631, 1632, 1632, 1633,\
|
||||
1633, 1634, 1635, 1635, 1636, 1636, 1637, 1637, 1638, 1639, 1639, 1640, 1640, 1641, 1641, 1642, 1643, 1643, 1644, 1644, 1645,\
|
||||
1645, 1646, 1646, 1647, 1648, 1648, 1649, 1649, 1650, 1650, 1651, 1651, 1652, 1652, 1653, 1654, 1654, 1655, 1655, 1656, 1656,\
|
||||
1657, 1657, 1658, 1658, 1659, 1659, 1660, 1660, 1661, 1662, 1662, 1663, 1663, 1664, 1664, 1665, 1665, 1666, 1666, 1667, 1667,\
|
||||
1668, 1668, 1669, 1669, 1670, 1670, 1671, 1671, 1672, 1672, 1673, 1673, 1674, 1674, 1675, 1675, 1676, 1676, 1677, 1677, 1678,\
|
||||
1678, 1679, 1679, 1680, 1680, 1681, 1681, 1682, 1682, 1683, 1683, 1684, 1684, 1685, 1685, 1686, 1686, 1687, 1687, 1688, 1688,\
|
||||
1689, 1689, 1689, 1690, 1690, 1691, 1691, 1692, 1692, 1693, 1693, 1694, 1694, 1695, 1695, 1696, 1696, 1697, 1697, 1697, 1698,\
|
||||
1698, 1699, 1699, 1700, 1700, 1701, 1701, 1702, 1702, 1703, 1703, 1703, 1704, 1704, 1705, 1705, 1706, 1706, 1707, 1707, 1707,\
|
||||
1708, 1708, 1709, 1709, 1710, 1710, 1711, 1711, 1711, 1712, 1712, 1713, 1713, 1714, 1714, 1715, 1715, 1715, 1716, 1716, 1717,\
|
||||
1717, 1718, 1718, 1718, 1719, 1719, 1720, 1720, 1721, 1721, 1721, 1722, 1722, 1723, 1723, 1724, 1724, 1724, 1725, 1725, 1726,\
|
||||
1726, 1726, 1727, 1727, 1728, 1728, 1729, 1729, 1729, 1730, 1730, 1731, 1731, 1731, 1732, 1732, 1733, 1733, 1733, 1734, 1734,\
|
||||
1735, 1735, 1736, 1736, 1736, 1737, 1737, 1738, 1738, 1738, 1739, 1739, 1740, 1740, 1740, 1741, 1741, 1742, 1742, 1742, 1743,\
|
||||
1743, 1744, 1744, 1744, 1745, 1745, 1745, 1746, 1746, 1747, 1747, 1747, 1748, 1748, 1749, 1749, 1749, 1750, 1750, 1751, 1751,\
|
||||
1751, 1752, 1752, 1752, 1753, 1753, 1754, 1754, 1754, 1755, 1755, 1755, 1756, 1756, 1757, 1757, 1757, 1758, 1758, 1758, 1759,\
|
||||
1759, 1760, 1760, 1760, 1761, 1761, 1761, 1762, 1762, 1763, 1763, 1763, 1764, 1764, 1764, 1765, 1765, 1766, 1766, 1766, 1767,\
|
||||
1767, 1767, 1768, 1768, 1768, 1769, 1769, 1769, 1770, 1770, 1771, 1771, 1771, 1772, 1772, 1772, 1773, 1773, 1773, 1774, 1774,\
|
||||
1774, 1775, 1775, 1776, 1776, 1776, 1777, 1777, 1777, 1778, 1778, 1778, 1779, 1779, 1779, 1780, 1780, 1780, 1781, 1781, 1781,\
|
||||
1782, 1782, 1782, 1783, 1783, 1784, 1784, 1784, 1785, 1785, 1785, 1786, 1786, 1786, 1787, 1787, 1787, 1788, 1788, 1788, 1789,\
|
||||
1789, 1789, 1790, 1790, 1790, 1791, 1791, 1791, 1792, 1792, 1792, 1793, 1793, 1793, 1794, 1794, 1794, 1795, 1795, 1795, 1796,\
|
||||
1796, 1796, 1797, 1797, 1797, 1798, 1798, 1798, 1799, 1799, 1799, 1800, 1800, 1800, 1800, 1801, 1801, 1801, 1802, 1802, 1802,\
|
||||
1803, 1803, 1803, 1804, 1804, 1804, 1805, 1805, 1805, 1806, 1806, 1806, 1807, 1807, 1807, 1808, 1808, 1808, 1808, 1809, 1809,\
|
||||
1809, 1810, 1810, 1810, 1811, 1811, 1811, 1812, 1812, 1812, 1813, 1813, 1813, 1813, 1814, 1814, 1814, 1815, 1815, 1815, 1816,\
|
||||
1816, 1816, 1816, 1817, 1817, 1817, 1818, 1818, 1818, 1819, 1819, 1819, 1820, 1820, 1820, 1820, 1821, 1821, 1821, 1822, 1822,\
|
||||
1822, 1823, 1823, 1823, 1823, 1824, 1824, 1824, 1825, 1825, 1825, 1826, 1826, 1826, 1826, 1827, 1827, 1827, 1828, 1828, 1828,\
|
||||
1828, 1829, 1829, 1829, 1830, 1830, 1830, 1831, 1831, 1831, 1831, 1832, 1832, 1832, 1833, 1833, 1833, 1833, 1834, 1834, 1834,\
|
||||
1835, 1835, 1835, 1835, 1836, 1836, 1836, 1837, 1837, 1837, 1837, 1838, 1838, 1838, 1839, 1839, 1839, 1839, 1840, 1840, 1840,\
|
||||
1841, 1841, 1841, 1841, 1842, 1842, 1842, 1842, 1843, 1843, 1843, 1844, 1844, 1844, 1844, 1845, 1845, 1845, 1846, 1846, 1846,\
|
||||
1846, 1847, 1847, 1847, 1847, 1848, 1848, 1848, 1849, 1849, 1849, 1849, 1850, 1850, 1850, 1850, 1851, 1851, 1851, 1852, 1852,\
|
||||
1852, 1852, 1853, 1853, 1853, 1853, 1854, 1854, 1854, 1854, 1855, 1855, 1855, 1856, 1856, 1856, 1856, 1857, 1857, 1857, 1857,\
|
||||
1858, 1858, 1858, 1858, 1859, 1859, 1859, 1860, 1860, 1860, 1860, 1861, 1861, 1861, 1861, 1862, 1862, 1862, 1862, 1863, 1863,\
|
||||
1863, 1863, 1864, 1864, 1864, 1864, 1865, 1865, 1865, 1865, 1866, 1866, 1866, 1867, 1867, 1867, 1867, 1868, 1868, 1868, 1868,\
|
||||
1869, 1869, 1869, 1869, 1870, 1870, 1870, 1870, 1871, 1871, 1871, 1871, 1872, 1872, 1872, 1872, 1873, 1873, 1873, 1873, 1874,\
|
||||
1874, 1874, 1874, 1875, 1875, 1875, 1875, 1876, 1876, 1876, 1876, 1877, 1877, 1877, 1877, 1878, 1878, 1878, 1878, 1879, 1879,\
|
||||
1879, 1879, 1880, 1880, 1880, 1880, 1881, 1881, 1881, 1881, 1882, 1882, 1882, 1882, 1882, 1883, 1883, 1883, 1883, 1884, 1884,\
|
||||
1884, 1884, 1885, 1885, 1885, 1885, 1886, 1886, 1886, 1886, 1887, 1887, 1887, 1887, 1888, 1888, 1888, 1888, 1888, 1889, 1889,\
|
||||
1889, 1889, 1890, 1890, 1890, 1890, 1891, 1891, 1891, 1891, 1892, 1892, 1892, 1892, 1892, 1893, 1893, 1893, 1893, 1894, 1894,\
|
||||
1894, 1894, 1895, 1895, 1895, 1895, 1896, 1896, 1896, 1896, 1896, 1897, 1897, 1897, 1897, 1898, 1898, 1898, 1898, 1899, 1899,\
|
||||
1899, 1899, 1899, 1900, 1900, 1900, 1900, 1901, 1901, 1901, 1901, 1901, 1902, 1902, 1902, 1902, 1903, 1903, 1903, 1903, 1904,\
|
||||
1904, 1904, 1904, 1904, 1905, 1905, 1905, 1905, 1906, 1906, 1906, 1906, 1906, 1907, 1907, 1907, 1907, 1908, 1908, 1908, 1908,\
|
||||
1908, 1909, 1909, 1909, 1909, 1910, 1910, 1910, 1910, 1910, 1911, 1911, 1911, 1911, 1912, 1912, 1912, 1912, 1912, 1913, 1913,\
|
||||
1913, 1913, 1913, 1914, 1914, 1914, 1914, 1915, 1915, 1915, 1915, 1915, 1916, 1916, 1916, 1916, 1917, 1917, 1917, 1917, 1917,\
|
||||
1918, 1918, 1918, 1918, 1918, 1919, 1919, 1919, 1919, 1920, 1920, 1920, 1920, 1920, 1921, 1921, 1921, 1921, 1921, 1922, 1922,\
|
||||
1922, 1922, 1922, 1923, 1923, 1923, 1923, 1924, 1924, 1924, 1924, 1924, 1925, 1925, 1925, 1925, 1925, 1926, 1926, 1926, 1926,\
|
||||
1926, 1927, 1927, 1927, 1927, 1927, 1928, 1928, 1928, 1928, 1929, 1929, 1929, 1929, 1929, 1930, 1930, 1930, 1930, 1930, 1931,\
|
||||
1931, 1931, 1931, 1931, 1932, 1932, 1932, 1932, 1932, 1933, 1933, 1933, 1933, 1933, 1934, 1934, 1934, 1934, 1934, 1935, 1935,\
|
||||
1935, 1935, 1935, 1936, 1936, 1936, 1936, 1936, 1937, 1937, 1937, 1937, 1937, 1938, 1938, 1938, 1938, 1938, 1939, 1939, 1939,\
|
||||
1939, 1939, 1940, 1940, 1940, 1940, 1940, 1941, 1941, 1941, 1941, 1941, 1942, 1942, 1942, 1942, 1942, 1943, 1943, 1943, 1943,\
|
||||
1943, 1944, 1944, 1944, 1944, 1944, 1945, 1945, 1945, 1945, 1945, 1946, 1946, 1946, 1946, 1946, 1947, 1947, 1947, 1947, 1947,\
|
||||
1947, 1948, 1948, 1948, 1948, 1948, 1949, 1949, 1949, 1949, 1949, 1950, 1950, 1950, 1950, 1950, 1951, 1951, 1951, 1951, 1951,\
|
||||
1952, 1952, 1952, 1952, 1952, 1952, 1953, 1953, 1953, 1953, 1953, 1954, 1954, 1954, 1954, 1954, 1955, 1955, 1955, 1955, 1955,\
|
||||
1956, 1956, 1956, 1956, 1956, 1956, 1957, 1957, 1957, 1957, 1957, 1958, 1958, 1958, 1958, 1958, 1958, 1959, 1959, 1959, 1959,\
|
||||
1959, 1960, 1960, 1960, 1960, 1960, 1961, 1961, 1961, 1961, 1961, 1961, 1962, 1962, 1962, 1962, 1962, 1963, 1963, 1963, 1963,\
|
||||
1963, 1963, 1964, 1964, 1964, 1964, 1964, 1965, 1965, 1965, 1965, 1965, 1965, 1966, 1966, 1966, 1966, 1966, 1967, 1967, 1967,\
|
||||
1967, 1967, 1967, 1968, 1968, 1968, 1968, 1968, 1969, 1969, 1969, 1969, 1969, 1969, 1970, 1970, 1970, 1970, 1970, 1971, 1971,\
|
||||
1971, 1971, 1971, 1971, 1972, 1972, 1972, 1972, 1972, 1972, 1973, 1973, 1973, 1973, 1973, 1974, 1974, 1974, 1974, 1974, 1974,\
|
||||
1975, 1975, 1975, 1975, 1975, 1975, 1976, 1976, 1976, 1976, 1976, 1977, 1977, 1977, 1977, 1977, 1977, 1978, 1978, 1978, 1978,\
|
||||
1978, 1978, 1979, 1979, 1979, 1979, 1979, 1979, 1980, 1980, 1980, 1980, 1980, 1980, 1981, 1981, 1981, 1981, 1981, 1982, 1982,\
|
||||
1982, 1982, 1982, 1982, 1983, 1983, 1983, 1983, 1983, 1983, 1984, 1984, 1984, 1984, 1984, 1984, 1985, 1985, 1985, 1985, 1985,\
|
||||
1985, 1986, 1986, 1986, 1986, 1986, 1986, 1987, 1987, 1987, 1987, 1987, 1987, 1988, 1988, 1988, 1988, 1988, 1988, 1989, 1989,\
|
||||
1989, 1989, 1989, 1989, 1990, 1990, 1990, 1990, 1990, 1990, 1991, 1991, 1991, 1991, 1991, 1991, 1992, 1992, 1992, 1992, 1992,\
|
||||
1992, 1993, 1993, 1993, 1993, 1993, 1993, 1994, 1994, 1994, 1994, 1994, 1994, 1995, 1995, 1995, 1995, 1995, 1995, 1996, 1996,\
|
||||
1996, 1996, 1996, 1996, 1997, 1997, 1997, 1997, 1997, 1997, 1998, 1998, 1998, 1998, 1998, 1998, 1999, 1999, 1999, 1999, 1999,\
|
||||
1999, 1999, 2000, 2000, 2000, 2000, 2000, 2000, 2001, 2001, 2001, 2001, 2001, 2001, 2002, 2002, 2002, 2002, 2002, 2002, 2003,\
|
||||
2003, 2003, 2003, 2003, 2003, 2003, 2004, 2004, 2004, 2004, 2004, 2004, 2005, 2005, 2005, 2005, 2005, 2005, 2006, 2006, 2006,\
|
||||
2006, 2006, 2006, 2006, 2007, 2007, 2007, 2007, 2007, 2007, 2008, 2008, 2008, 2008, 2008, 2008, 2009, 2009, 2009, 2009, 2009,\
|
||||
2009, 2009, 2010, 2010, 2010, 2010, 2010, 2010, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2012, 2012, 2012, 2012, 2012, 2012,\
|
||||
2013, 2013, 2013, 2013, 2013, 2013, 2013, 2014, 2014, 2014, 2014, 2014, 2014, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2016,\
|
||||
2016, 2016, 2016, 2016, 2016, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2018, 2018, 2018, 2018, 2018, 2018, 2019, 2019, 2019,\
|
||||
2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2021, 2021, 2021, 2021, 2021, 2021, 2022, 2022, 2022, 2022,\
|
||||
2022, 2022, 2022, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2025, 2025, 2025, 2025,\
|
||||
2025, 2025, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2028, 2028, 2028, 2028, 2028,\
|
||||
2028, 2028, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2031, 2031, 2031, 2031, 2031,\
|
||||
2031, 2031, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2034, 2034, 2034, 2034, 2034,\
|
||||
2034, 2034, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2037, 2037, 2037, 2037, 2037,\
|
||||
2037, 2037, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2040, 2040, 2040, 2040};
|
||||
|
||||
static int dump_i2c_reg(struct ti_lmu_bl_chip *chip)
|
||||
{
|
||||
struct regmap *regmap = chip->lmu->regmap;
|
||||
int ret_val = 0;
|
||||
|
||||
regmap_read(regmap, 0x10, &ret_val);
|
||||
pr_err("[bkl] %s read 0x10 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x1A, &ret_val);
|
||||
pr_err("[bkl] %s read 0x1A = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x1C, &ret_val);
|
||||
pr_err("[bkl] %s read 0x1C = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x22, &ret_val);
|
||||
pr_err("[bkl] %s read 0x22 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x23, &ret_val);
|
||||
pr_err("[bkl] %s read 0x23 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x24, &ret_val);
|
||||
pr_err("[bkl] %s read 0x24 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x18, &ret_val);
|
||||
pr_err("[bkl] %s read 0x18 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x16, &ret_val);
|
||||
pr_err("[bkl] %s read 0x16 = 0x%x\n", __func__, ret_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ti_hbm_set(enum backlight_hbm_mode hbm_mode)
|
||||
{
|
||||
struct regmap *regmap = bl_chip->lmu->regmap;
|
||||
int value = 0;
|
||||
|
||||
pr_err("[bkl] %s enter\n", __func__);
|
||||
|
||||
switch (hbm_mode) {
|
||||
case HBM_MODE_DEFAULT:
|
||||
regmap_write(regmap, 0x18, 0x15);//21.8mA
|
||||
pr_err("This is hbm mode 1\n");
|
||||
break;
|
||||
case HBM_MODE_LEVEL1:
|
||||
regmap_write(regmap, 0x18, 0x19);//25mA
|
||||
pr_err("This is hbm mode 2\n");
|
||||
break;
|
||||
case HBM_MODE_LEVEL2:
|
||||
regmap_write(regmap, 0x18, 0x1C);//27.4mA
|
||||
pr_err("This is hbm mode 3\n");
|
||||
break;
|
||||
case HBM_MODE_LEVEL3:
|
||||
regmap_write(regmap, 0x18, 0x17);//23.4mA
|
||||
pr_err("This is hbm mode 4\n");
|
||||
break;
|
||||
case HBM_MODE_LEVEL4:
|
||||
regmap_write(regmap, 0x18, 0x18);//24.2mA
|
||||
pr_err("This is hbm mode 5\n");
|
||||
break;
|
||||
default:
|
||||
pr_err("This isn't hbm mode\n");
|
||||
break;
|
||||
}
|
||||
|
||||
regmap_read(regmap, 0x18, &value);
|
||||
pr_err("[bkl]%s hbm_mode = %d,regmap value=0x%x\n", __func__, hbm_mode, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_enable(struct ti_lmu_bl *lmu_bl, int enable)
|
||||
{
|
||||
struct ti_lmu_bl_chip *chip = lmu_bl->chip;
|
||||
struct regmap *regmap = chip->lmu->regmap;
|
||||
unsigned long enable_time = chip->cfg->reginfo->enable_usec;
|
||||
u8 *reg = chip->cfg->reginfo->enable;
|
||||
|
||||
if (!reg)
|
||||
return -EINVAL;
|
||||
|
||||
if (enable)
|
||||
return regmap_write(regmap, *reg, 0x02);
|
||||
else
|
||||
return regmap_write(regmap, *reg, 0x00);
|
||||
|
||||
if (enable_time > 0)
|
||||
usleep_range(enable_time, enable_time + 100);
|
||||
}
|
||||
|
||||
static void ti_lmu_backlight_pwm_ctrl(struct ti_lmu_bl *lmu_bl, int brightness,
|
||||
int max_brightness)
|
||||
{
|
||||
struct pwm_device *pwm;
|
||||
unsigned int duty, period;
|
||||
|
||||
if (!lmu_bl->pwm) {
|
||||
pwm = devm_pwm_get(lmu_bl->chip->dev, DEFAULT_PWM_NAME);
|
||||
if (IS_ERR(pwm)) {
|
||||
dev_err(lmu_bl->chip->dev,
|
||||
"Can not get PWM device, err: %ld\n",
|
||||
PTR_ERR(pwm));
|
||||
return;
|
||||
}
|
||||
|
||||
lmu_bl->pwm = pwm;
|
||||
}
|
||||
|
||||
period = lmu_bl->pwm_period;
|
||||
duty = brightness * period / max_brightness;
|
||||
|
||||
pwm_config(lmu_bl->pwm, duty, period);
|
||||
if (duty)
|
||||
pwm_enable(lmu_bl->pwm);
|
||||
else
|
||||
pwm_disable(lmu_bl->pwm);
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_update_brightness_register(struct ti_lmu_bl *lmu_bl,
|
||||
int brightness)
|
||||
{
|
||||
const struct ti_lmu_bl_cfg *cfg = lmu_bl->chip->cfg;
|
||||
const struct ti_lmu_bl_reg *reginfo = cfg->reginfo;
|
||||
struct regmap *regmap = lmu_bl->chip->lmu->regmap;
|
||||
u8 reg, val;
|
||||
int ret;
|
||||
// int i = 0;
|
||||
|
||||
regmap_write(regmap, 0x13, 0x11);
|
||||
|
||||
if (lmu_bl->mode == BL_PWM_BASED) {
|
||||
switch (cfg->pwm_action) {
|
||||
case UPDATE_PWM_ONLY:
|
||||
/* No register update is required */
|
||||
return 0;
|
||||
case UPDATE_MAX_BRT:
|
||||
/*
|
||||
* PWM can start from any non-zero code and dim down
|
||||
* to zero. So, brightness register should be updated
|
||||
* even in PWM mode.
|
||||
*/
|
||||
if (brightness > 0)
|
||||
brightness = MAX_BRIGHTNESS_11BIT;
|
||||
else
|
||||
brightness = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Brightness register update
|
||||
*
|
||||
* 11 bit dimming: update LSB bits and write MSB byte.
|
||||
* MSB brightness should be shifted.
|
||||
* 8 bit dimming: write MSB byte.
|
||||
*/
|
||||
if (brightness < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!reginfo->brightness_msb)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->max_brightness == MAX_BRIGHTNESS_11BIT) {
|
||||
if (!reginfo->brightness_lsb)
|
||||
return -EINVAL;
|
||||
|
||||
if(lmu_bl->map_type == EXPONENTIAL_TYPE && lmu_bl->led_current_align) {
|
||||
if(brightness) {
|
||||
pr_info("[bkl] %s brightness = %d, align = %d\n", __func__,
|
||||
brightness, lmu_bl->led_current_align);
|
||||
brightness = brightness*8383/10000+324;
|
||||
}
|
||||
}
|
||||
|
||||
reg = reginfo->brightness_lsb[lmu_bl->bank_id];
|
||||
ret = regmap_update_bits(regmap, reg,
|
||||
LMU_BACKLIGHT_11BIT_LSB_MASK,
|
||||
brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
pr_info("[bkl][after]11bit %s brightness = %d\n", __func__, brightness);
|
||||
val = (brightness >> LMU_BACKLIGHT_11BIT_MSB_SHIFT) & 0xFF;
|
||||
} else {
|
||||
val = brightness & 0xFF;
|
||||
pr_err("[bkl]8bit %s val = %d\n", __func__, val);
|
||||
}
|
||||
|
||||
reg = reginfo->brightness_msb[lmu_bl->bank_id];
|
||||
ret = regmap_write(regmap, reg, val);
|
||||
return ret;
|
||||
}
|
||||
static int ti_lmu_backlight_set_brightness(int brightness)
|
||||
{
|
||||
struct ti_lmu_bl *lmu_bl = bl_chip->lmu_bl;
|
||||
int ret;
|
||||
|
||||
if (brightness > 0)
|
||||
ret = ti_lmu_backlight_enable(lmu_bl, 1);
|
||||
else
|
||||
ret = ti_lmu_backlight_enable(lmu_bl, 0);
|
||||
|
||||
if (ret) {
|
||||
pr_err("[bkl] %s enable failed ret %d \n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ti_lmu_backlight_update_brightness_register(lmu_bl, brightness);
|
||||
}
|
||||
|
||||
int lm3697_set_brightness(int brightness)
|
||||
{
|
||||
printk(KERN_INFO "[bkl][before]%s brightness = %d\n", __func__, brightness);
|
||||
|
||||
return ti_lmu_backlight_set_brightness(brightness);
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_get_brightness(struct backlight_device *bl_dev)
|
||||
{
|
||||
return bl_dev->props.brightness;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_update_status(struct backlight_device *bl_dev)
|
||||
{
|
||||
struct ti_lmu_bl *lmu_bl = bl_get_data(bl_dev);
|
||||
int brightness = bl_dev->props.brightness;
|
||||
int ret;
|
||||
|
||||
if (bl_dev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
|
||||
brightness = 0;
|
||||
|
||||
if (brightness > 0)
|
||||
ret = ti_lmu_backlight_enable(lmu_bl, 1);
|
||||
else
|
||||
ret = ti_lmu_backlight_enable(lmu_bl, 0);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (lmu_bl->mode == BL_PWM_BASED)
|
||||
ti_lmu_backlight_pwm_ctrl(lmu_bl, brightness,
|
||||
bl_dev->props.max_brightness);
|
||||
|
||||
return ti_lmu_backlight_update_brightness_register(lmu_bl, brightness);
|
||||
}
|
||||
|
||||
static const struct backlight_ops lmu_backlight_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.update_status = ti_lmu_backlight_update_status,
|
||||
.get_brightness = ti_lmu_backlight_get_brightness,
|
||||
};
|
||||
|
||||
static int ti_lmu_backlight_of_get_ctrl_bank(struct device_node *np,
|
||||
struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
const char *name;
|
||||
u32 *sources;
|
||||
int num_channels = lmu_bl->chip->cfg->num_channels;
|
||||
int ret, num_sources;
|
||||
|
||||
sources = devm_kzalloc(lmu_bl->chip->dev, num_channels, GFP_KERNEL);
|
||||
if (!sources)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!of_property_read_string(np, "label", &name))
|
||||
lmu_bl->name = name;
|
||||
else
|
||||
lmu_bl->name = np->name;
|
||||
|
||||
ret = of_property_count_u32_elems(np, "led-sources");
|
||||
if (ret < 0 || ret > num_channels)
|
||||
return -EINVAL;
|
||||
num_sources = ret;
|
||||
ret = of_property_read_u32_array(np, "led-sources", sources,
|
||||
num_sources);
|
||||
if (ret){
|
||||
return ret;
|
||||
}
|
||||
lmu_bl->led_sources = 0;
|
||||
while (num_sources--)
|
||||
set_bit(sources[num_sources], &lmu_bl->led_sources);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ti_lmu_backlight_of_get_light_properties(struct device_node *np,
|
||||
struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
of_property_read_u32(np, "default-brightness-level",
|
||||
&lmu_bl->default_brightness);
|
||||
|
||||
of_property_read_u32(np, "ramp-up-msec", &lmu_bl->ramp_up_msec);
|
||||
of_property_read_u32(np, "ramp-down-msec", &lmu_bl->ramp_down_msec);
|
||||
|
||||
if(of_property_read_u32(np, "current-mode", &lmu_bl->led_current_mode)) {
|
||||
lmu_bl->led_current_mode = HBM_MODE_DEFAULT;
|
||||
pr_info("[bkl] %s led_current_mode default %d\n", __func__,
|
||||
lmu_bl->led_current_mode);
|
||||
}
|
||||
|
||||
if(of_property_read_u32(np, "boost-ovp", &lmu_bl->led_boost_ovp)) {
|
||||
lmu_bl->led_boost_ovp = BOOST_OVP_32V;
|
||||
pr_info("[bkl] %s led_boost_ovp default %d\n", __func__,
|
||||
lmu_bl->led_boost_ovp);
|
||||
}
|
||||
|
||||
if(of_property_read_u32(np, "boost-freq", &lmu_bl->led_boost_freq)) {
|
||||
lmu_bl->led_boost_freq = BOOST_FREQ_500K;
|
||||
pr_info("[bkl] %s led_boost_freq default %d\n", __func__,
|
||||
lmu_bl->led_boost_freq);
|
||||
}
|
||||
|
||||
|
||||
if(of_property_read_u32(np, "map-type", &lmu_bl->map_type)) {
|
||||
lmu_bl->map_type = LINEAR_TYPE;
|
||||
pr_info("[bkl] %s map_type default %d\n", __func__,
|
||||
lmu_bl->map_type);
|
||||
}
|
||||
|
||||
if(of_property_read_u32(np, "current-align-type", &lmu_bl->led_current_align)) {
|
||||
lmu_bl->led_current_align = ALIGN_NONE;
|
||||
pr_info("[bkl] %s led_current_align default %d, no align, keep as lm3697 exp mode\n", __func__,
|
||||
lmu_bl->led_current_align);
|
||||
}
|
||||
}
|
||||
|
||||
static void ti_lmu_backlight_of_get_brightness_mode(struct device_node *np,
|
||||
struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
of_property_read_u32(np, "pwm-period", &lmu_bl->pwm_period);
|
||||
|
||||
if (lmu_bl->pwm_period > 0)
|
||||
lmu_bl->mode = BL_PWM_BASED;
|
||||
else
|
||||
lmu_bl->mode = BL_REGISTER_BASED;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_of_create(struct ti_lmu_bl_chip *chip,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct ti_lmu_bl *lmu_bl, *each;
|
||||
int ret, num_backlights;
|
||||
int i = 0;
|
||||
|
||||
num_backlights = of_get_child_count(np);
|
||||
if (num_backlights == 0) {
|
||||
dev_err(chip->dev, "No backlight strings\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* One chip can have mulitple backlight strings */
|
||||
lmu_bl = devm_kzalloc(chip->dev, sizeof(*lmu_bl) * num_backlights,
|
||||
GFP_KERNEL);
|
||||
if (!lmu_bl)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Child is mapped to LMU backlight control bank */
|
||||
for_each_child_of_node(np, child) {
|
||||
each = lmu_bl + i;
|
||||
each->bank_id = i;
|
||||
each->chip = chip;
|
||||
|
||||
ret = ti_lmu_backlight_of_get_ctrl_bank(child, each);
|
||||
if (ret) {
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ti_lmu_backlight_of_get_light_properties(child, each);
|
||||
ti_lmu_backlight_of_get_brightness_mode(child, each);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
chip->lmu_bl = lmu_bl;
|
||||
chip->num_backlights = num_backlights;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_update_ctrl_mode(struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
struct regmap *regmap = lmu_bl->chip->lmu->regmap;
|
||||
u32 *reg = lmu_bl->chip->cfg->reginfo->mode + lmu_bl->bank_id;
|
||||
u8 val;
|
||||
|
||||
if (!reg)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Update PWM configuration register.
|
||||
* If the mode is register based, then clear the bit.
|
||||
*/
|
||||
if (lmu_bl->mode == BL_PWM_BASED)
|
||||
val = LMU_BL_GET_VAL(*reg);
|
||||
else
|
||||
val = 0;
|
||||
|
||||
return regmap_update_bits(regmap, LMU_BL_GET_ADDR(*reg),
|
||||
LMU_BL_GET_MASK(*reg), val);
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_convert_ramp_to_index(struct ti_lmu_bl *lmu_bl,
|
||||
enum ti_lmu_bl_ramp_mode mode)
|
||||
{
|
||||
const int *ramp_table = lmu_bl->chip->cfg->ramp_table;
|
||||
const int size = lmu_bl->chip->cfg->size_ramp;
|
||||
unsigned int msec;
|
||||
int i;
|
||||
|
||||
if (!ramp_table)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mode) {
|
||||
case BL_RAMP_UP:
|
||||
msec = lmu_bl->ramp_up_msec;
|
||||
break;
|
||||
case BL_RAMP_DOWN:
|
||||
msec = lmu_bl->ramp_down_msec;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (msec <= ramp_table[0])
|
||||
return 0;
|
||||
|
||||
if (msec > ramp_table[size - 1])
|
||||
return size - 1;
|
||||
|
||||
for (i = 1; i < size; i++) {
|
||||
if (msec == ramp_table[i])
|
||||
return i;
|
||||
|
||||
/* Find an approximate index by looking up the table */
|
||||
if (msec > ramp_table[i - 1] && msec < ramp_table[i]) {
|
||||
if (msec - ramp_table[i - 1] < ramp_table[i] - msec)
|
||||
return i - 1;
|
||||
else
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_set_ramp(struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
struct regmap *regmap = lmu_bl->chip->lmu->regmap;
|
||||
const struct ti_lmu_bl_reg *reginfo = lmu_bl->chip->cfg->reginfo;
|
||||
int offset = reginfo->ramp_reg_offset;
|
||||
int i, ret, index;
|
||||
u32 reg;
|
||||
|
||||
for (i = BL_RAMP_UP; i <= BL_RAMP_DOWN; i++) {
|
||||
index = ti_lmu_backlight_convert_ramp_to_index(lmu_bl, i);
|
||||
if (index > 0) {
|
||||
if (!reginfo->ramp)
|
||||
break;
|
||||
|
||||
if (lmu_bl->bank_id == 0)
|
||||
reg = reginfo->ramp[i];
|
||||
else
|
||||
reg = reginfo->ramp[i] + offset;
|
||||
|
||||
/*
|
||||
* Note that the result of LMU_BL_GET_VAL() is
|
||||
* shift bit. So updated bit is shifted index value.
|
||||
*/
|
||||
ret = regmap_update_bits(regmap, LMU_BL_GET_ADDR(reg),
|
||||
LMU_BL_GET_MASK(reg),
|
||||
index << LMU_BL_GET_VAL(reg));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_configure(struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ti_lmu_backlight_update_ctrl_mode(lmu_bl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ti_lmu_backlight_set_ramp(lmu_bl);
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_init(struct ti_lmu_bl_chip *chip)
|
||||
{
|
||||
struct regmap *regmap = chip->lmu->regmap;
|
||||
unsigned char boost_ctl;
|
||||
unsigned char brightness_cfg;
|
||||
pr_err("[bkl] %s enter\n", __func__);
|
||||
/*
|
||||
* 'init' register data consists of address, mask, value.
|
||||
* Driver can get each data by using LMU_BL_GET_ADDR(),
|
||||
* LMU_BL_GET_MASK(), LMU_BL_GET_VAL().
|
||||
*/
|
||||
|
||||
boost_ctl = (chip->lmu_bl->led_boost_ovp<<1) | chip->lmu_bl->led_boost_freq;
|
||||
brightness_cfg = chip->lmu_bl->map_type;
|
||||
|
||||
regmap_write(regmap, 0x10, 0x07);
|
||||
regmap_write(regmap, 0x12, 0x11);
|
||||
regmap_write(regmap, 0x16, brightness_cfg);
|
||||
regmap_write(regmap, 0x19, 0x07);
|
||||
regmap_write(regmap, 0x18, 0x15);//21.8mA default
|
||||
regmap_write(regmap, 0x1A, boost_ctl);
|
||||
regmap_write(regmap, 0x1C, 0x0E);
|
||||
//regmap_write(regmap, 0x22, 0x07);
|
||||
//regmap_write(regmap, 0x23, 0xFF);
|
||||
regmap_write(regmap, 0x24, 0x02);
|
||||
regmap_write(regmap, 0xB4, 0x03);
|
||||
|
||||
|
||||
pr_err("[bkl] %s finish\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_reload(struct ti_lmu_bl_chip *chip)
|
||||
{
|
||||
struct ti_lmu_bl *each;
|
||||
int i, ret;
|
||||
|
||||
ret = ti_lmu_backlight_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < chip->num_backlights; i++) {
|
||||
each = chip->lmu_bl + i;
|
||||
ret = ti_lmu_backlight_configure(each);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
backlight_update_status(each->bl_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_add_device(struct device *dev,
|
||||
struct ti_lmu_bl *lmu_bl)
|
||||
{
|
||||
struct backlight_device *bl_dev;
|
||||
struct backlight_properties props;
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.brightness = lmu_bl->default_brightness;
|
||||
props.max_brightness = lmu_bl->chip->cfg->max_brightness;
|
||||
|
||||
bl_dev = backlight_device_register(LM3697_NAME, dev, lmu_bl,
|
||||
&lmu_backlight_ops, &props);
|
||||
if (IS_ERR(bl_dev))
|
||||
return PTR_ERR(bl_dev);
|
||||
|
||||
lmu_bl->bl_dev = bl_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t i2c_reg_dump_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct regmap *regmap = bl_chip->lmu->regmap;
|
||||
int ret_val = 0;
|
||||
|
||||
regmap_read(regmap, 0x00, &ret_val);
|
||||
pr_err("[bkl] %s read 0x00 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x01, &ret_val);
|
||||
pr_err("[bkl] %s read 0x01 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x10, &ret_val);
|
||||
pr_err("[bkl] %s read 0x10 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x11, &ret_val);
|
||||
pr_err("[bkl] %s read 0x11 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x12, &ret_val);
|
||||
pr_err("[bkl] %s read 0x12 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x13, &ret_val);
|
||||
pr_err("[bkl] %s read 0x13 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x14, &ret_val);
|
||||
pr_err("[bkl] %s read 0x14 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x15, &ret_val);
|
||||
pr_err("[bkl] %s read 0x15 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x16, &ret_val);
|
||||
pr_err("[bkl] %s read 0x16 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x17, &ret_val);
|
||||
pr_err("[bkl] %s read 0x17 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x18, &ret_val);
|
||||
pr_err("[bkl] %s read 0x18 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x19, &ret_val);
|
||||
pr_err("[bkl] %s read 0x19 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x1A, &ret_val);
|
||||
pr_err("[bkl] %s read 0x1A = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x1B, &ret_val);
|
||||
pr_err("[bkl] %s read 0x1B = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x1C, &ret_val);
|
||||
pr_err("[bkl] %s read 0x1C = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x20, &ret_val);
|
||||
pr_err("[bkl] %s read 0x20 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x21, &ret_val);
|
||||
pr_err("[bkl] %s read 0x21 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x22, &ret_val);
|
||||
pr_err("[bkl] %s read 0x22 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x23, &ret_val);
|
||||
pr_err("[bkl] %s read 0x23 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0x24, &ret_val);
|
||||
pr_err("[bkl] %s read 0x24 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0xB0, &ret_val);
|
||||
pr_err("[bkl] %s read 0xB0 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0xB2, &ret_val);
|
||||
pr_err("[bkl] %s read 0xB2 = 0x%x\n", __func__, ret_val);
|
||||
regmap_read(regmap, 0xB4, &ret_val);
|
||||
pr_err("[bkl] %s read 0xB4 = 0x%x\n", __func__, ret_val);
|
||||
|
||||
ret = snprintf(buf, PAGE_SIZE, "reg dump done \n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEVICE_ATTR(i2c_reg_dump, S_IRUGO, i2c_reg_dump_show, NULL);
|
||||
|
||||
static int ti_lm3697_read_chipid(struct ti_lmu_bl_chip *chip)
|
||||
{
|
||||
int ret_val = 0;
|
||||
unsigned char cnt = 0;
|
||||
struct regmap *regmap = chip->lmu->regmap;
|
||||
|
||||
while (cnt < 5)
|
||||
{
|
||||
regmap_read(regmap, 0x00, &ret_val);
|
||||
switch (ret_val)
|
||||
{
|
||||
case 0x01:
|
||||
pr_info("%s ti_lm3697 detected\n",__func__);
|
||||
return 0;
|
||||
default:
|
||||
pr_info("%s unsupported device revision (0x%x)\n",
|
||||
__func__, ret_val);
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
msleep(2);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
static struct ti_lmu_bl_chip *
|
||||
ti_lmu_backlight_register(struct device *dev, struct ti_lmu *lmu,
|
||||
const struct ti_lmu_bl_cfg *cfg)
|
||||
{
|
||||
struct ti_lmu_bl_chip *chip;
|
||||
struct ti_lmu_bl *each;
|
||||
int i, ret;
|
||||
|
||||
pr_debug("[bkl] %s enter\n", __func__);
|
||||
|
||||
if (!cfg) {
|
||||
dev_err(dev, "Operation is not configured\n");
|
||||
goto err_ein;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), 0);
|
||||
if (!chip)
|
||||
goto err_eno;
|
||||
|
||||
chip->dev = dev;
|
||||
chip->lmu = lmu;
|
||||
chip->cfg = cfg;
|
||||
|
||||
ret = ti_lmu_backlight_of_create(chip, dev->of_node);
|
||||
if (ret){
|
||||
goto err_init;
|
||||
}
|
||||
|
||||
ret = ti_lm3697_read_chipid(chip);
|
||||
if (ret < 0)
|
||||
{
|
||||
pr_err("%s : ID idenfy failed\n", __func__);
|
||||
goto err_init;
|
||||
}
|
||||
|
||||
for (i = 0; i < chip->num_backlights; i++) {
|
||||
each = chip->lmu_bl + i;
|
||||
ret = ti_lmu_backlight_configure(each);
|
||||
if (ret) {
|
||||
dev_err(dev, "[bkl] Backlight config err: %d\n", ret);
|
||||
goto err_init;
|
||||
}
|
||||
ret = ti_lmu_backlight_add_device(dev, each);
|
||||
if (ret) {
|
||||
dev_err(dev, "[bkl] Backlight device err: %d\n", ret);
|
||||
goto err_init;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ti_lmu_backlight_init(chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "Backlight init err: %d\n", ret);
|
||||
goto err_init;
|
||||
}
|
||||
|
||||
bl_chip = chip;
|
||||
|
||||
ti_hbm_set(chip->lmu_bl->led_current_mode);
|
||||
|
||||
dump_i2c_reg(chip);
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_i2c_reg_dump);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to create dev_attr_i2c_reg_dump\n");
|
||||
}
|
||||
|
||||
pr_err("[bkl] %s finish\n", __func__);
|
||||
|
||||
return chip;
|
||||
|
||||
err_init:
|
||||
if(chip->lmu_bl)
|
||||
devm_kfree(dev, chip->lmu_bl);
|
||||
if(chip)
|
||||
devm_kfree(dev, chip);
|
||||
err_ein:
|
||||
gpio_free(lmu->en_gpio);
|
||||
return ERR_PTR(-EINVAL);
|
||||
err_eno:
|
||||
gpio_free(lmu->en_gpio);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static void ti_lmu_backlight_unregister(struct ti_lmu_bl_chip *chip)
|
||||
{
|
||||
struct ti_lmu_bl *each;
|
||||
int i;
|
||||
|
||||
/* Turn off the brightness */
|
||||
for (i = 0; i < chip->num_backlights; i++) {
|
||||
each = chip->lmu_bl + i;
|
||||
each->bl_dev->props.brightness = 0;
|
||||
backlight_update_status(each->bl_dev);
|
||||
backlight_device_unregister(each->bl_dev);
|
||||
}
|
||||
device_remove_file(chip->dev, &dev_attr_i2c_reg_dump);
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_monitor_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *unused)
|
||||
{
|
||||
struct ti_lmu_bl_chip *chip = container_of(nb, struct ti_lmu_bl_chip,
|
||||
nb);
|
||||
|
||||
if (action == LMU_EVENT_MONITOR_DONE) {
|
||||
if (ti_lmu_backlight_reload(chip))
|
||||
return NOTIFY_STOP;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ti_lmu *lmu = dev_get_drvdata(dev->parent);
|
||||
struct ti_lmu_bl_chip *chip;
|
||||
int ret;
|
||||
|
||||
pr_err("[bkl] %s enter\n", __func__);
|
||||
|
||||
|
||||
chip = ti_lmu_backlight_register(dev, lmu, &lmu_bl_cfg[pdev->id]);
|
||||
if (IS_ERR(chip)) {
|
||||
pr_err("[bkl] %s error bkl register\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
/*
|
||||
* Notifier callback is required because backlight device needs
|
||||
* reconfiguration after fault detection procedure is done by
|
||||
* ti-lmu-fault-monitor driver.
|
||||
*/
|
||||
if (chip->cfg->fault_monitor_used) {
|
||||
chip->nb.notifier_call = ti_lmu_backlight_monitor_notifier;
|
||||
ret = blocking_notifier_chain_register(&chip->lmu->notifier,
|
||||
&chip->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
pr_err("[bkl] %s finish\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_lmu_backlight_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_lmu_bl_chip *chip = platform_get_drvdata(pdev);
|
||||
|
||||
if (chip->cfg->fault_monitor_used)
|
||||
blocking_notifier_chain_unregister(&chip->lmu->notifier,
|
||||
&chip->nb);
|
||||
|
||||
ti_lmu_backlight_unregister(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ti_lmu_backlight_driver = {
|
||||
.probe = ti_lmu_backlight_probe,
|
||||
.remove = ti_lmu_backlight_remove,
|
||||
.driver = {
|
||||
.name = "ti-lmu-backlight",
|
||||
},
|
||||
};
|
||||
|
||||
int ti_lmu_backlight_device_init(void)
|
||||
{
|
||||
return platform_driver_register(&ti_lmu_backlight_driver);
|
||||
}
|
||||
EXPORT_SYMBOL(ti_lmu_backlight_device_init);
|
||||
void ti_lmu_backlight_exit(void)
|
||||
{
|
||||
return platform_driver_unregister(&ti_lmu_backlight_driver);
|
||||
}
|
||||
EXPORT_SYMBOL(ti_lmu_backlight_exit);
|
||||
// module_platform_driver(ti_lmu_backlight_driver)
|
||||
|
||||
MODULE_DESCRIPTION("TI LMU Backlight Driver");
|
||||
MODULE_AUTHOR("Milo Kim");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:ti-lmu-backlight");
|
||||
82
drivers/backlight/lm3697/ti_lm3697_backlight_data.c
Normal file
82
drivers/backlight/lm3697/ti_lm3697_backlight_data.c
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* TI LMU (Lighting Management Unit) Backlight Device Data
|
||||
*
|
||||
* Copyright 2016 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "ti_lm3697.h"
|
||||
#include "ti_lm3697_regulator.h"
|
||||
#include "ti-lmu.h"
|
||||
|
||||
/* LM3697 */
|
||||
static u32 lm3697_init_regs[] = {
|
||||
//LM3697_INIT_RAMP_SELECT,
|
||||
LM3697_REG_HVLED_OUTPUT_CFG,
|
||||
LM3697_REG_BOOST_CFG,
|
||||
LM3697_REG_PWM_CFG,
|
||||
LM3697_REG_BRT_B_LSB,
|
||||
LM3697_REG_BRT_B_MSB,
|
||||
LM3697_REG_ENABLE,
|
||||
};
|
||||
|
||||
static u32 lm3697_channel_regs[] = {
|
||||
LM3697_CHANNEL_1,
|
||||
LM3697_CHANNEL_2,
|
||||
LM3697_CHANNEL_3,
|
||||
};
|
||||
|
||||
static u32 lm3697_mode_regs[] = {
|
||||
LM3697_MODE_PWM_B,
|
||||
};
|
||||
|
||||
static u32 lm3697_ramp_regs[] = {
|
||||
LM3697_RAMPUP,
|
||||
LM3697_RAMPDN,
|
||||
};
|
||||
|
||||
static u8 lm3697_enable_reg = LM3697_REG_ENABLE;
|
||||
|
||||
static u8 lm3697_brightness_msb_regs[] = {
|
||||
LM3697_REG_BRT_B_MSB,
|
||||
};
|
||||
|
||||
static u8 lm3697_brightness_lsb_regs[] = {
|
||||
LM3697_REG_BRT_B_LSB,
|
||||
};
|
||||
|
||||
static const struct ti_lmu_bl_reg lm3697_reg_info = {
|
||||
.init = lm3697_init_regs,
|
||||
.num_init = ARRAY_SIZE(lm3697_init_regs),
|
||||
.channel = lm3697_channel_regs,
|
||||
.mode = lm3697_mode_regs,
|
||||
.ramp = lm3697_ramp_regs,
|
||||
.ramp_reg_offset = 1, /* For LM3697_REG_BL1_RAMPUP/DN */
|
||||
.enable = &lm3697_enable_reg,
|
||||
.brightness_msb = lm3697_brightness_msb_regs,
|
||||
.brightness_lsb = lm3697_brightness_lsb_regs,
|
||||
};
|
||||
|
||||
static int common_ramp_table[] = {
|
||||
2, 250, 500, 1000, 2000, 4000, 8000, 16000,
|
||||
};
|
||||
|
||||
struct ti_lmu_bl_cfg lmu_bl_cfg[LMU_MAX_ID] = {
|
||||
{
|
||||
.reginfo = &lm3697_reg_info,
|
||||
.num_channels = LM3697_MAX_CHANNELS,
|
||||
.max_brightness = MAX_BRIGHTNESS_11BIT,
|
||||
.pwm_action = UPDATE_PWM_AND_BRT_REGISTER,
|
||||
.ramp_table = common_ramp_table,
|
||||
.size_ramp = ARRAY_SIZE(common_ramp_table),
|
||||
.fault_monitor_used = true,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(lmu_bl_cfg);
|
||||
63
drivers/backlight/lm3697/ti_lm3697_regulator.h
Normal file
63
drivers/backlight/lm3697/ti_lm3697_regulator.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* TI LMU (Lighting Management Unit) Device Register Map
|
||||
*
|
||||
* Copyright 2017 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MFD_TI_LMU_REGISTER_H__
|
||||
#define __MFD_TI_LMU_REGISTER_H__
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/* LM3697 */
|
||||
#define LM3697_REG_HVLED_OUTPUT_CFG 0x10
|
||||
#define LM3697_HVLED1_CFG_MASK BIT(0)
|
||||
#define LM3697_HVLED2_CFG_MASK BIT(1)
|
||||
#define LM3697_HVLED3_CFG_MASK BIT(2)
|
||||
#define LM3697_HVLED1_CFG_SHIFT 0
|
||||
#define LM3697_HVLED2_CFG_SHIFT 1
|
||||
#define LM3697_HVLED3_CFG_SHIFT 2
|
||||
|
||||
#define LM3697_REG_BL0_RAMP 0x11
|
||||
#define LM3697_REG_BL1_RAMP 0x12
|
||||
#define LM3697_RAMPUP_MASK 0xF0
|
||||
#define LM3697_RAMPUP_SHIFT 4
|
||||
#define LM3697_RAMPDN_MASK 0x0F
|
||||
#define LM3697_RAMPDN_SHIFT 0
|
||||
|
||||
#define LM3697_REG_RAMP_CONF 0x14
|
||||
#define LM3697_RAMP_MASK 0x0F
|
||||
#define LM3697_RAMP_EACH 0x05
|
||||
|
||||
#define LM3697_REG_BOOST_CFG 0x1A
|
||||
|
||||
#define LM3697_REG_PWM_CFG 0x1C
|
||||
#define LM3697_PWM_A_MASK BIT(0)
|
||||
#define LM3697_PWM_B_MASK BIT(1)
|
||||
|
||||
#define LM3697_REG_IMAX_A 0x17
|
||||
#define LM3697_REG_IMAX_B 0x18
|
||||
|
||||
#define LM3697_REG_FEEDBACK_ENABLE 0x19
|
||||
|
||||
#define LM3697_REG_BRT_A_LSB 0x20
|
||||
#define LM3697_REG_BRT_A_MSB 0x21
|
||||
#define LM3697_REG_BRT_B_LSB 0x22
|
||||
#define LM3697_REG_BRT_B_MSB 0x23
|
||||
|
||||
#define LM3697_REG_ENABLE 0x24
|
||||
|
||||
#define LM3697_REG_OPEN_FAULT_STATUS 0xB0
|
||||
|
||||
#define LM3697_REG_SHORT_FAULT_STATUS 0xB2
|
||||
|
||||
#define LM3697_REG_MONITOR_ENABLE 0xB4
|
||||
|
||||
#define LM3697_MAX_REG 0xB4
|
||||
#endif
|
||||
8
drivers/backlight/sm5350/Android.mk
Normal file
8
drivers/backlight/sm5350/Android.mk
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := sm5350_bl.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
5
drivers/backlight/sm5350/Kbuild
Normal file
5
drivers/backlight/sm5350/Kbuild
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m += sm5350_bl.o
|
||||
|
||||
523
drivers/backlight/sm5350/sm5350_bl.c
Normal file
523
drivers/backlight/sm5350/sm5350_bl.c
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
/*
|
||||
* SM5350 BL Driver
|
||||
*
|
||||
* SiliconMitus SM5350 Backlight driver chip
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/backlight.h>
|
||||
|
||||
#define SM5350_NAME "sm5350-bl"
|
||||
|
||||
#define MAX_BRIGHTNESS (2047)
|
||||
#define BANK_NONE 0x00
|
||||
#define BANK_A 0x01
|
||||
#define BANK_B 0x02
|
||||
|
||||
#define SM5350_HVLED_CURR_SINK_OUT_CFG 0x07
|
||||
#define SM5350_BRIGHTNESS_CFG 0x01
|
||||
|
||||
#define SM5350_REVISION_REG 0x00
|
||||
#define SM5350_SW_RESET_REG 0x01
|
||||
#define SM5350_HVLED_CURR_SINK_OUT_CFG_REG 0x10
|
||||
#define SM5350_CTL_A_RAMP_TIME_REG 0x11
|
||||
#define SM5350_CTL_B_RAMP_TIME_REG 0x12
|
||||
#define SM5350_CTL_RUNTIME_RAMP_TIME_REG 0x13
|
||||
#define SM5350_CTL_RUNTIME_RAMP_CFG_REG 0x14
|
||||
#define SM5350_BRIGHTNESS_CFG_REG 0x16
|
||||
#define SM5350_CTL_A_FULL_SCALE_CURR_REG 0x17
|
||||
#define SM5350_CTL_B_FULL_SCALE_CURR_REG 0x18
|
||||
#define SM5350_HVLED_CURR_SINK_FEEDBACK_REG 0x19
|
||||
#define SM5350_BOOST_CTL_REG 0x1A
|
||||
#define SM5350_AUTO_FREQ_THRESHOLD_REG 0x1B
|
||||
#define SM5350_PWM_CFG_REG 0x1C
|
||||
#define SM5350_CTL_A_BRIGHTNESS_LSB_REG 0x20
|
||||
#define SM5350_CTL_A_BRIGHTNESS_MSB_REG 0x21
|
||||
#define SM5350_CTL_B_BRIGHTNESS_LSB_REG 0x22
|
||||
#define SM5350_CTL_B_BRIGHTNESS_MSB_REG 0x23
|
||||
#define SM5350_CTL_B_BANK_EN_REG 0x24
|
||||
#define SM5350_HVLED_OPEN_FAULTS_REG 0xB0
|
||||
#define SM5350_HVLED_SHORT_FAULTS_REG 0xB2
|
||||
#define SM5350_LED_FAULT_ENABLES_REG 0xB4
|
||||
|
||||
enum backlight_exp_current_align {
|
||||
ALIGN_NONE,
|
||||
ALIGN_AW99703
|
||||
};
|
||||
|
||||
struct sm5350_data {
|
||||
struct i2c_client *client;
|
||||
struct i2c_adapter *adapter;
|
||||
unsigned short addr;
|
||||
struct work_struct work;
|
||||
bool enable;
|
||||
bool bank_A;
|
||||
bool bank_B;
|
||||
u8 ctl_bank_en;
|
||||
u8 pwm_cfg;
|
||||
u8 boost_ctl;
|
||||
u8 full_scale_current;
|
||||
u8 map_mode;
|
||||
unsigned int led_current_align; /* Align boost current to AW chip */
|
||||
unsigned int default_brightness;
|
||||
bool brt_code_enable;
|
||||
u16 *brt_code_table;
|
||||
int en_gpio;
|
||||
struct backlight_device *bl_dev;
|
||||
};
|
||||
|
||||
struct sm5350_data *g_sm5350_data;
|
||||
|
||||
static int platform_read_i2c_block(struct i2c_client *client, char *writebuf,
|
||||
int writelen, char *readbuf, int readlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (writelen > 0) {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = writelen,
|
||||
.buf = writebuf,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = readlen,
|
||||
.buf = readbuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 2);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: i2c read error.\n",
|
||||
__func__);
|
||||
} else {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = readlen,
|
||||
.buf = readbuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s:i2c read error.\n", __func__);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5350_read_reg(struct i2c_client *client, u8 addr, u8 *val)
|
||||
{
|
||||
return platform_read_i2c_block(client, &addr, 1, val, 1);
|
||||
}
|
||||
|
||||
static int platform_write_i2c_block(struct i2c_client *client, char *writebuf, int writelen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = writelen,
|
||||
.buf = writebuf,
|
||||
},
|
||||
};
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: i2c write error.\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5350_write_reg(struct i2c_client *client, u8 addr, const u8 val)
|
||||
{
|
||||
u8 buf[2] = {0};
|
||||
|
||||
buf[0] = addr;
|
||||
buf[1] = val;
|
||||
return platform_write_i2c_block(client, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int sm5350_init_registers(struct sm5350_data *drvdata)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
sm5350_write_reg(drvdata->client, SM5350_BOOST_CTL_REG, drvdata->boost_ctl);
|
||||
sm5350_write_reg(drvdata->client, SM5350_PWM_CFG_REG, drvdata->pwm_cfg);
|
||||
sm5350_write_reg(drvdata->client, SM5350_CTL_B_BANK_EN_REG, drvdata->ctl_bank_en);
|
||||
sm5350_write_reg(drvdata->client, SM5350_BRIGHTNESS_CFG_REG, drvdata->map_mode);
|
||||
sm5350_write_reg(drvdata->client, SM5350_HVLED_CURR_SINK_OUT_CFG_REG, SM5350_HVLED_CURR_SINK_OUT_CFG);
|
||||
sm5350_write_reg(drvdata->client, SM5350_CTL_B_FULL_SCALE_CURR_REG, drvdata->full_scale_current);
|
||||
|
||||
drvdata->enable = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sm5350_bl_get_brightness(struct backlight_device *bl_dev)
|
||||
{
|
||||
return bl_dev->props.brightness;
|
||||
}
|
||||
|
||||
int sm5350_set_brightness(struct sm5350_data *drvdata, int brt_val)
|
||||
{
|
||||
u8 brt_LSB = 0;
|
||||
u8 brt_MSB = 0;
|
||||
int index = 0, remainder;
|
||||
int code, code1, code2;
|
||||
printk("%s backlight_val = %d\n",__func__, brt_val);
|
||||
|
||||
if ((drvdata->map_mode == 0) && (drvdata->led_current_align == ALIGN_AW99703))
|
||||
brt_val = brt_val*8383/10000+324;
|
||||
|
||||
if (drvdata->brt_code_enable) {
|
||||
index = brt_val / 10;
|
||||
remainder = brt_val % 10;
|
||||
|
||||
code1 = drvdata->brt_code_table[index];
|
||||
code2 = drvdata->brt_code_table[index+1];
|
||||
|
||||
code = (code2 - code1) * remainder / 10 + code1;
|
||||
|
||||
brt_LSB = code % 0x7;
|
||||
brt_MSB = (code >> 3) & 0xFF;
|
||||
printk("brt_LSB_1 %x, brt_MSB_1 %x\n", brt_LSB, brt_MSB);
|
||||
} else {
|
||||
brt_LSB = brt_val & 0x7;
|
||||
brt_MSB = (brt_val >> 3) & 0xFF;
|
||||
}
|
||||
printk("brt_LSB %x, brt_MSB %x\n", brt_LSB, brt_MSB);
|
||||
|
||||
if (drvdata->enable == false)
|
||||
sm5350_init_registers(drvdata);
|
||||
|
||||
if (drvdata->bank_B) {
|
||||
sm5350_write_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_LSB_REG, brt_LSB);
|
||||
sm5350_write_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_MSB_REG, brt_MSB);
|
||||
}
|
||||
|
||||
if (brt_val == 0)
|
||||
drvdata->enable = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sm5350_bl_update_status(struct backlight_device *bl_dev)
|
||||
{
|
||||
struct sm5350_data *drvdata = bl_get_data(bl_dev);
|
||||
int brt;
|
||||
|
||||
if (bl_dev->props.state & BL_CORE_SUSPENDED)
|
||||
bl_dev->props.brightness = 0;
|
||||
|
||||
brt = bl_dev->props.brightness;
|
||||
/*
|
||||
* Brightness register should always be written
|
||||
* not only register based mode but also in PWM mode.
|
||||
*/
|
||||
return sm5350_set_brightness(drvdata, brt);
|
||||
}
|
||||
|
||||
static const struct backlight_ops sm5350_bl_ops = {
|
||||
.update_status = sm5350_bl_update_status,
|
||||
.get_brightness = sm5350_bl_get_brightness,
|
||||
};
|
||||
|
||||
static void dump_sm5350_regs(struct sm5350_data *drvdata)
|
||||
{
|
||||
u8 brt_LSB = 0;
|
||||
u8 brt_MSB = 0;
|
||||
u8 boost_ctl, pwm_cfg, ctl_bank_en, full_scale_current, revision;
|
||||
|
||||
sm5350_read_reg(drvdata->client, SM5350_BOOST_CTL_REG, &boost_ctl);
|
||||
sm5350_read_reg(drvdata->client, SM5350_PWM_CFG_REG, &pwm_cfg);
|
||||
sm5350_read_reg(drvdata->client, SM5350_CTL_B_BANK_EN_REG, &ctl_bank_en);
|
||||
sm5350_read_reg(drvdata->client, SM5350_CTL_A_FULL_SCALE_CURR_REG, &full_scale_current);
|
||||
sm5350_read_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_LSB_REG, &brt_LSB);
|
||||
sm5350_read_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_MSB_REG, &brt_MSB);
|
||||
sm5350_read_reg(drvdata->client, SM5350_REVISION_REG, &revision);
|
||||
|
||||
pr_err(">>-- boost_ctl(0x%x), pwm_cfg(0x%x), ctl_bank_en(0x%x), full_scale_current(0x%x), brt_LSB(0x%x), brt_MSB(0x%x), revision(0x%x).\n",
|
||||
boost_ctl, pwm_cfg, ctl_bank_en, full_scale_current, brt_LSB, brt_MSB, revision);
|
||||
}
|
||||
|
||||
static int sm5350_get_dt_data(struct device *dev, struct sm5350_data *drvdata)
|
||||
{
|
||||
int rc;
|
||||
u32 tmp;
|
||||
struct device_node *of_node = NULL;
|
||||
int len;
|
||||
const char *data;
|
||||
u32 *buf;
|
||||
int i = 0;
|
||||
|
||||
of_node = dev->of_node;
|
||||
|
||||
drvdata->en_gpio = of_get_named_gpio(of_node, "hwen-gpio", 0);
|
||||
if (drvdata->en_gpio < 0) {
|
||||
pr_err("%s,dt not get en_gpio, en_gpio = %d\n", __func__, drvdata->en_gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = of_property_read_u32(of_node, "boost-ctl", &tmp);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, dt not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->boost_ctl = (!rc ? tmp : 0);
|
||||
pr_debug("%s : boost_ctl=0x%x\n",__func__, drvdata->boost_ctl);
|
||||
|
||||
rc = of_property_read_u32(of_node, "map-mode", &tmp);
|
||||
drvdata->map_mode= (!rc ? tmp : 1); /* 1: linear, 0: expo, linear as default*/
|
||||
pr_debug("%s : map_mode=0x%x\n",__func__, drvdata->map_mode);
|
||||
|
||||
if (of_property_read_u32(of_node, "current-align-type", &drvdata->led_current_align))
|
||||
drvdata->led_current_align = ALIGN_NONE;
|
||||
pr_debug("%s : led_current_align=0x%x\n",__func__, drvdata->led_current_align);
|
||||
|
||||
rc = of_property_read_u32(of_node, "sm5350,default-brightness", &tmp);
|
||||
drvdata->default_brightness= (!rc ? tmp : MAX_BRIGHTNESS);
|
||||
pr_debug("%s : default_brightness=0x%x\n",__func__, drvdata->default_brightness);
|
||||
|
||||
rc = of_property_read_u32(of_node, "pwm-cfg", &tmp);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, dt not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->pwm_cfg = (!rc ? tmp : 0);
|
||||
pr_debug("%s : pwm_cfg=0x%x\n",__func__, drvdata->pwm_cfg);
|
||||
|
||||
rc = of_property_read_u32(of_node, "ctl-bank-en", &tmp);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, dt not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->ctl_bank_en = (!rc ? tmp : 0);
|
||||
pr_debug("%s : ctl_bank_en=0x%x\n",__func__, drvdata->ctl_bank_en);
|
||||
|
||||
if (drvdata->ctl_bank_en & 0x01)
|
||||
drvdata->bank_A = true;
|
||||
if (drvdata->ctl_bank_en & 0x02)
|
||||
drvdata->bank_B = true;
|
||||
|
||||
pr_debug("%s : bank_A=%d bank_B=%d\n",__func__, drvdata->bank_A, drvdata->bank_B);
|
||||
|
||||
rc = of_property_read_u32(of_node, "full-scale-current", &tmp);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, dt not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->full_scale_current = (!rc ? tmp : 0);
|
||||
|
||||
pr_info("bank_A = %d, bank_B = %d, pwm_cfg = 0x%x, full_scale_current = 0x%x, map_mode = 0x%x, boost_ctl = 0x%x.\n",
|
||||
drvdata->bank_A, drvdata->bank_B, drvdata->pwm_cfg, drvdata->full_scale_current, drvdata->map_mode, drvdata->boost_ctl);
|
||||
|
||||
drvdata->brt_code_enable = of_property_read_bool(of_node, "brt-code-enable");
|
||||
|
||||
if (drvdata->brt_code_enable == false) {
|
||||
pr_info("%s : brt_code_enable = %d, rc = %d.\n",__func__, drvdata->brt_code_enable, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
data = of_get_property(of_node, "brt-code-table", &len);
|
||||
if (!data) {
|
||||
pr_err("%s: read brt-code-table failed\n", __func__);
|
||||
//return -ENOMEM;
|
||||
}
|
||||
|
||||
len /= sizeof(u32);
|
||||
|
||||
buf = kzalloc(len * sizeof(u32), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32_array(of_node, "brt-code-table", buf, len);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, dt not specified\n",__func__, __LINE__);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
drvdata->brt_code_table = kzalloc(len * sizeof(u16), GFP_KERNEL);
|
||||
if (!drvdata->brt_code_table) {
|
||||
pr_err("%s:%d, allocate memory failed\n",__func__, __LINE__);
|
||||
rc = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i=0; i < len; i++) {
|
||||
drvdata->brt_code_table[i] = (u16) buf[i];
|
||||
pr_debug("%s : buf=%d i=%d\n",__func__, buf[i], i);
|
||||
}
|
||||
end:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sm5350_bl_enable_hw(struct sm5350_data *drvdata){
|
||||
|
||||
return gpio_request_one(drvdata->en_gpio, GPIOF_OUT_INIT_HIGH, "sm5350_hwen");
|
||||
|
||||
}
|
||||
|
||||
static int sm5350_read_revision(struct sm5350_data *drvdata)
|
||||
{
|
||||
int ret = -1;
|
||||
u8 value = 0;
|
||||
unsigned char cnt = 0;
|
||||
|
||||
while (cnt < 5) {
|
||||
ret = sm5350_read_reg(drvdata->client, SM5350_REVISION_REG, &value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to read reg SM5350_REVISION_REG: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
switch (value) {
|
||||
case 0x00:
|
||||
pr_info("%s sm5350 detected\n", __func__);
|
||||
return 0;
|
||||
default:
|
||||
pr_info("%s unsupported device revision (0x%x)\n",
|
||||
__func__, value);
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
|
||||
msleep(2);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sm5350_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct sm5350_data *drvdata;
|
||||
struct backlight_device *bl_dev;
|
||||
struct backlight_properties props;
|
||||
int err = 0;
|
||||
|
||||
printk("%s entry\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
pr_err("%s : I2C_FUNC_I2C not supported\n", __func__);
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!client->dev.of_node) {
|
||||
pr_err("%s : no device node\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
drvdata = kzalloc(sizeof(struct sm5350_data), GFP_KERNEL);
|
||||
if (drvdata == NULL) {
|
||||
pr_err("%s : kzalloc failed\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
drvdata->client = client;
|
||||
drvdata->adapter = client->adapter;
|
||||
drvdata->addr = client->addr;
|
||||
drvdata->enable = true;
|
||||
|
||||
g_sm5350_data = drvdata;
|
||||
|
||||
err = sm5350_get_dt_data(&client->dev, drvdata);
|
||||
if(err < 0) {
|
||||
pr_err("%s : get dt failed\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto err_init;
|
||||
}
|
||||
err = sm5350_bl_enable_hw(drvdata);
|
||||
if (err)
|
||||
goto err_init;
|
||||
|
||||
i2c_set_clientdata(client, drvdata);
|
||||
|
||||
/*sm5350 read revision*/
|
||||
err = sm5350_read_revision(drvdata);
|
||||
if (err < 0) {
|
||||
pr_err("%s : ID idenfy failed\n", __func__);
|
||||
goto err_init;
|
||||
}
|
||||
/*sm5350 read revision*/
|
||||
printk("sm-sm5350 detected success\n");
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.brightness = MAX_BRIGHTNESS;
|
||||
props.max_brightness = MAX_BRIGHTNESS;
|
||||
bl_dev = backlight_device_register(SM5350_NAME, &client->dev,
|
||||
drvdata, &sm5350_bl_ops, &props);
|
||||
|
||||
if (IS_ERR(bl_dev))
|
||||
return PTR_ERR(bl_dev);
|
||||
drvdata->bl_dev = bl_dev;
|
||||
sm5350_init_registers(drvdata);
|
||||
dump_sm5350_regs(drvdata);
|
||||
sm5350_set_brightness(drvdata, drvdata->default_brightness);
|
||||
|
||||
printk("sm-sm5350 probe okay\n");
|
||||
return 0;
|
||||
|
||||
err_init:
|
||||
kfree(drvdata);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sm5350_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sm5350_data *drvdata = i2c_get_clientdata(client);
|
||||
|
||||
backlight_device_unregister(drvdata->bl_dev);
|
||||
kfree(drvdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sm5350_id[] = {
|
||||
{SM5350_NAME, 0},
|
||||
{}
|
||||
};
|
||||
static struct of_device_id match_table[] = {
|
||||
{.compatible = "sm-sm5350",}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, sm5350_id);
|
||||
|
||||
static struct i2c_driver sm5350_i2c_driver = {
|
||||
.probe = sm5350_probe,
|
||||
.remove = sm5350_remove,
|
||||
.id_table = sm5350_id,
|
||||
.driver = {
|
||||
.name = SM5350_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = match_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(sm5350_i2c_driver);
|
||||
MODULE_DESCRIPTION("Back Light driver for SM5350");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
8
drivers/ese/st54spi_gpio/Android.mk
Normal file
8
drivers/ese/st54spi_gpio/Android.mk
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := st54spi_gpio.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
6
drivers/ese/st54spi_gpio/Kbuild
Normal file
6
drivers/ese/st54spi_gpio/Kbuild
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m += st54spi_gpio.o
|
||||
|
||||
16
drivers/ese/st54spi_gpio/Makefile
Normal file
16
drivers/ese/st54spi_gpio/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
all: modules
|
||||
|
||||
modules:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
|
||||
|
||||
%:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
|
||||
rm -rf .tmp_versions
|
||||
|
||||
|
||||
303
drivers/ese/st54spi_gpio/st54spi_gpio.c
Normal file
303
drivers/ese/st54spi_gpio/st54spi_gpio.c
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ST54SPI GPIO driver
|
||||
* Copyright (C) 2021 ST Microelectronics S.A.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* * 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
**********************************************************************************/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Flag ESE_CONF_GPIO_OPEN_RELEASE is to configure nRESET GPIO in
|
||||
open function and freeing GPIO in release function */
|
||||
#define ESE_CONF_GPIO_OPEN_RELEASE
|
||||
|
||||
/* Flag ESE_CONF_GPIO_PROBE_REMOVE is to configure
|
||||
nRESET GPIO in probe function */
|
||||
//#define ESE_CONF_GPIO_PROBE_REMOVE
|
||||
|
||||
#define ST54SPI_GPIO__MAGIC 0xEB
|
||||
#define ST54SPI_GET_GPIO _IOW(ST54SPI_GPIO__MAGIC, 0x01, unsigned int)
|
||||
#define ST54SPI_SET_GPIO _IOW(ST54SPI_GPIO__MAGIC, 0x02, unsigned int)
|
||||
|
||||
struct st54spi_gpio_device {
|
||||
dev_t st54spi_gpio_dev_t;
|
||||
struct cdev c_dev;
|
||||
struct class *class;
|
||||
struct device *device;
|
||||
struct spi_device *spi_dev;
|
||||
/* GPIO for st54spi Reset pin (output) */
|
||||
int gpiod_reset;
|
||||
};
|
||||
|
||||
/** @brief IOCTL function to be used to set or get data from upper layer.
|
||||
*
|
||||
* @param pfile fil node for opened device.
|
||||
* @cmd IOCTL type from upper layer.
|
||||
* @arg IOCTL arg from upper layer.
|
||||
*
|
||||
* @return 0 on success, error code for failures.
|
||||
*/
|
||||
long st54spi_gpio_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct st54spi_gpio_device *st54spi_gpio_dev = pfile->private_data;
|
||||
|
||||
if (!st54spi_gpio_dev) {
|
||||
pr_err("%s ENODEV! st54spi_gpio_dev is NULL\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case ST54SPI_GET_GPIO:
|
||||
ret = gpio_get_value(st54spi_gpio_dev->gpiod_reset);
|
||||
break;
|
||||
case ST54SPI_SET_GPIO:
|
||||
if ((arg == 0) || (arg == 1)) {
|
||||
gpio_set_value(st54spi_gpio_dev->gpiod_reset, arg);
|
||||
} else {
|
||||
pr_err("%s bad arg %lu\n", __func__, arg);
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("%s Unsupported ioctl cmd 0x%x, arg %lu\n",
|
||||
__func__, cmd, arg);
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function will be called when we open the Device file*/
|
||||
|
||||
static int st54spi_gpio_dev_open(struct inode *inode, struct file *pfile)
|
||||
{
|
||||
#ifdef ESE_CONF_GPIO_OPEN_RELEASE
|
||||
int rc;
|
||||
#endif
|
||||
struct st54spi_gpio_device *st54spi_gpio_dev = container_of(inode->i_cdev,
|
||||
struct st54spi_gpio_device, c_dev);
|
||||
|
||||
pr_info("%s : Device File Opened\n", __func__);
|
||||
if (!st54spi_gpio_dev) {
|
||||
pr_err("%s ENODEV NULL\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
#ifdef ESE_CONF_GPIO_OPEN_RELEASE
|
||||
rc = gpio_request(st54spi_gpio_dev->gpiod_reset, "gpio-power_nreset");
|
||||
if (rc < 0) {
|
||||
pr_err("%s: request gpio failed: %d\n", __func__, rc);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rc = gpio_direction_output(st54spi_gpio_dev->gpiod_reset, 0);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: gpio cannot set the output %d\n", __func__, rc);
|
||||
gpio_free(st54spi_gpio_dev->gpiod_reset);
|
||||
return -EFAULT;
|
||||
}
|
||||
#endif
|
||||
|
||||
pfile->private_data = st54spi_gpio_dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function will be called when we close the Device file*/
|
||||
|
||||
static int st54spi_gpio_dev_release(struct inode *inode, struct file *pfile)
|
||||
{
|
||||
#ifdef ESE_CONF_GPIO_OPEN_RELEASE
|
||||
struct st54spi_gpio_device *st54spi_gpio_dev = pfile->private_data;
|
||||
|
||||
if (gpio_is_valid(st54spi_gpio_dev->gpiod_reset))
|
||||
gpio_free(st54spi_gpio_dev->gpiod_reset);
|
||||
#endif
|
||||
pr_info("%s: Device File Closed\n", __func__);
|
||||
pfile->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations st54spi_gpio_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = st54spi_gpio_dev_open,
|
||||
.release = st54spi_gpio_dev_release,
|
||||
.unlocked_ioctl = st54spi_gpio_dev_ioctl,
|
||||
};
|
||||
|
||||
/* This function will be called to probe the character device*/
|
||||
static int st54spi_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
struct st54spi_gpio_device *st54spi_gpio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (np == NULL) {
|
||||
pr_err("%s : struct np is null\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("%s : enter", __func__);
|
||||
st54spi_gpio_dev = devm_kzalloc(dev, sizeof(*st54spi_gpio_dev), GFP_KERNEL);
|
||||
if (!st54spi_gpio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create device node */
|
||||
rc = alloc_chrdev_region(&st54spi_gpio_dev->st54spi_gpio_dev_t, 0, 1, "st54spi_gpio");
|
||||
if (rc < 0) {
|
||||
pr_err("%s: alloc_chrdev_region() failed\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
st54spi_gpio_dev->class = class_create(THIS_MODULE, "st54spi_gpio");
|
||||
if (IS_ERR(st54spi_gpio_dev->class)) {
|
||||
rc = PTR_ERR(st54spi_gpio_dev->class);
|
||||
pr_err("%s: Error creating st54spi_gpio_dev->class: %d\n", __func__, rc);
|
||||
goto fail_class_create;
|
||||
}
|
||||
|
||||
cdev_init(&st54spi_gpio_dev->c_dev, &st54spi_gpio_dev_fops);
|
||||
rc = cdev_add(&st54spi_gpio_dev->c_dev, st54spi_gpio_dev->st54spi_gpio_dev_t, 1);
|
||||
if (rc) {
|
||||
pr_err("%s: Error calling cdev_add: %d\n", __func__, rc);
|
||||
goto fail_cdev_add;
|
||||
}
|
||||
|
||||
st54spi_gpio_dev->device = device_create(st54spi_gpio_dev->class,
|
||||
NULL, st54spi_gpio_dev->st54spi_gpio_dev_t,
|
||||
st54spi_gpio_dev, "st54spi_gpio");
|
||||
if (IS_ERR(st54spi_gpio_dev->device)) {
|
||||
rc = PTR_ERR(st54spi_gpio_dev->device);
|
||||
pr_err("%s: device_create failed: %d\n", __func__, rc);
|
||||
goto fail_device_create;
|
||||
}
|
||||
|
||||
/* Setup gpio-power_nreset */
|
||||
st54spi_gpio_dev->gpiod_reset = of_get_named_gpio(np, "gpio-power_nreset", 0);
|
||||
if (!gpio_is_valid(st54spi_gpio_dev->gpiod_reset)) {
|
||||
pr_err("%s : Unable to request gpio-power_nreset\n", __func__);
|
||||
rc = -EFAULT;
|
||||
goto fail_gpiod_get;
|
||||
}
|
||||
|
||||
#ifdef ESE_CONF_GPIO_PROBE_REMOVE
|
||||
rc = gpio_request(st54spi_gpio_dev->gpiod_reset, "gpio-power_nreset");
|
||||
if (rc < 0) {
|
||||
pr_err("%s: request gpio failed: %d\n", __func__, rc);
|
||||
rc = -EFAULT;
|
||||
goto fail_gpiod_get;
|
||||
}
|
||||
|
||||
rc = gpio_direction_output(st54spi_gpio_dev->gpiod_reset, 0);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: gpio cannot set the output %d\n", __func__, rc);
|
||||
rc = -EFAULT;
|
||||
goto fail_gpiod_request;
|
||||
}
|
||||
#endif
|
||||
|
||||
platform_set_drvdata(pdev, st54spi_gpio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
#ifdef ESE_CONF_GPIO_PROBE_REMOVE
|
||||
fail_gpiod_request:
|
||||
gpio_free(st54spi_gpio_dev->gpiod_reset);
|
||||
#endif
|
||||
fail_gpiod_get:
|
||||
device_destroy(st54spi_gpio_dev->class, st54spi_gpio_dev->st54spi_gpio_dev_t);
|
||||
fail_device_create:
|
||||
cdev_del(&st54spi_gpio_dev->c_dev);
|
||||
fail_cdev_add:
|
||||
class_destroy(st54spi_gpio_dev->class);
|
||||
fail_class_create:
|
||||
unregister_chrdev_region(st54spi_gpio_dev->st54spi_gpio_dev_t, 1);
|
||||
devm_kfree(dev, st54spi_gpio_dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This function will be called to remove the character device*/
|
||||
static int st54spi_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct st54spi_gpio_device *st54spi_gpio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pr_info("%s : enter", __func__);
|
||||
st54spi_gpio_dev = platform_get_drvdata(pdev);
|
||||
device_destroy(st54spi_gpio_dev->class, st54spi_gpio_dev->st54spi_gpio_dev_t);
|
||||
unregister_chrdev_region(st54spi_gpio_dev->st54spi_gpio_dev_t, 1);
|
||||
class_destroy(st54spi_gpio_dev->class);
|
||||
cdev_del(&st54spi_gpio_dev->c_dev);
|
||||
devm_kfree(dev, st54spi_gpio_dev);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
#ifdef ESE_CONF_GPIO_PROBE_REMOVE
|
||||
if (gpio_is_valid(st54spi_gpio_dev->gpiod_reset))
|
||||
gpio_free(st54spi_gpio_dev->gpiod_reset);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id st54spi_gpio_of_match[] = {
|
||||
{ .compatible = "st,st54spi_gpio", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st54spi_gpio_of_match);
|
||||
|
||||
static struct platform_driver st54spi_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "st54spi_gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = st54spi_gpio_of_match,
|
||||
},
|
||||
.probe = st54spi_gpio_probe,
|
||||
.remove = st54spi_gpio_remove,
|
||||
};
|
||||
|
||||
/* module load/unload record keeping */
|
||||
static int __init st54spi_gpio_dev_init(void)
|
||||
{
|
||||
pr_info("%s : Loading st54spi gpio_driver 1.0\n", __func__);
|
||||
return platform_driver_register(&st54spi_gpio_driver);
|
||||
}
|
||||
|
||||
module_init(st54spi_gpio_dev_init);
|
||||
|
||||
static void __exit st54spi_gpio_dev_exit(void)
|
||||
{
|
||||
pr_info("%s : Unloading st54spi gpio_driver 1.0\n", __func__);
|
||||
platform_driver_unregister(&st54spi_gpio_driver);
|
||||
}
|
||||
|
||||
module_exit(st54spi_gpio_dev_exit);
|
||||
|
||||
MODULE_AUTHOR("STMicroelectronics");
|
||||
MODULE_DESCRIPTION("ST54SPI GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("st54spi_gpio");
|
||||
8
drivers/ese/st54x/Android.mk
Normal file
8
drivers/ese/st54x/Android.mk
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := st54spi.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
6
drivers/ese/st54x/Kbuild
Normal file
6
drivers/ese/st54x/Kbuild
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m += st54spi.o
|
||||
|
||||
16
drivers/ese/st54x/Makefile
Normal file
16
drivers/ese/st54x/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
all: modules
|
||||
|
||||
modules:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
|
||||
|
||||
%:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
|
||||
rm -rf .tmp_versions
|
||||
|
||||
|
||||
1240
drivers/ese/st54x/st54spi.c
Normal file
1240
drivers/ese/st54x/st54spi.c
Normal file
File diff suppressed because it is too large
Load diff
10
drivers/fm/Android.mk
Normal file
10
drivers/fm/Android.mk
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
ifeq ($(BOARD_HAS_FM_ELNA), true)
|
||||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := fm_ctrl.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
endif
|
||||
4
drivers/fm/Kbuild
Normal file
4
drivers/fm/Kbuild
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
|
||||
obj-m += fm_ctrl.o
|
||||
11
drivers/fm/Makefile
Normal file
11
drivers/fm/Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
||||
|
||||
236
drivers/fm/fm_ctrl.c
Normal file
236
drivers/fm/fm_ctrl.c
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Motorola Mobility LLC
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
|
||||
#define DRIVER_VERSION "0.0.1"
|
||||
|
||||
struct fm_ctrl_drvdata {
|
||||
struct device *dev;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pstate_default;
|
||||
struct pinctrl_state *pstate_active;
|
||||
struct pinctrl_state *pstate_suspend;
|
||||
bool factory_mode;
|
||||
};
|
||||
|
||||
|
||||
static bool mmi_factory_check(void)
|
||||
{
|
||||
struct device_node *np = of_find_node_by_path("/chosen");
|
||||
bool factory = false;
|
||||
|
||||
if (np)
|
||||
factory = of_property_read_bool(np, "mmi,factory-cable");
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
static ssize_t device_name_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fm_ctrl_drvdata *data = dev_get_drvdata(dev);
|
||||
|
||||
if (!data) {
|
||||
pr_err("fm_ctrl drvdata is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "moto fm control intf\n");
|
||||
}
|
||||
|
||||
|
||||
static ssize_t elna_en_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fm_ctrl_drvdata *data = dev_get_drvdata(dev);
|
||||
|
||||
if (!data) {
|
||||
pr_err("fm_ctrl drvdata is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "fm_ctrl: not support!\n");
|
||||
}
|
||||
|
||||
static ssize_t elna_en_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct fm_ctrl_drvdata *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
unsigned int res = 0;
|
||||
|
||||
if (!data) {
|
||||
pr_err("fm_ctrl drvdata is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = kstrtouint(buf, 0, &res);
|
||||
if(ret) {
|
||||
pr_err("fm_ctrl failed to get data, set as default!\n");
|
||||
}
|
||||
if(1 == res) {
|
||||
ret = pinctrl_select_state(data->pinctrl, data->pstate_active);
|
||||
}
|
||||
else {
|
||||
ret = pinctrl_select_state(data->pinctrl, data->pstate_suspend);
|
||||
}
|
||||
if(ret) {
|
||||
pr_err("fm_ctrl failed to set pinctrl!\n");
|
||||
}
|
||||
else {
|
||||
pr_info("fm_ctrl set elan=%u\n", res);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR(device_name, 0444, device_name_read, NULL);
|
||||
static DEVICE_ATTR(elna_en, 0644, elna_en_read, elna_en_write);
|
||||
|
||||
static struct attribute *fm_ctrl_sysfs_attrs[] = {
|
||||
&dev_attr_device_name.attr,
|
||||
&dev_attr_elna_en.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group fm_ctrl_sysfs_attr_grp = {
|
||||
.attrs = fm_ctrl_sysfs_attrs,
|
||||
};
|
||||
|
||||
|
||||
static int fm_ctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fm_ctrl_drvdata *drvdata;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s begin\n", __func__);
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
/* Get pinctrl if target uses pinctrl */
|
||||
drvdata->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR_OR_NULL(drvdata->pinctrl)) {
|
||||
ret = PTR_ERR(drvdata->pinctrl);
|
||||
pr_err("%s: Pincontrol DT property returned %X\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->pstate_default = pinctrl_lookup_state(drvdata->pinctrl,
|
||||
"default");
|
||||
if (IS_ERR_OR_NULL(drvdata->pstate_default)) {
|
||||
ret = PTR_ERR(drvdata->pstate_default);
|
||||
pr_err("Can not lookup default pinstate %d\n", ret);
|
||||
return -ENOENT;
|
||||
}
|
||||
drvdata->pstate_active = pinctrl_lookup_state(drvdata->pinctrl,
|
||||
"elna_active");
|
||||
if (IS_ERR_OR_NULL(drvdata->pstate_active)) {
|
||||
ret = PTR_ERR(drvdata->pstate_active);
|
||||
pr_err("Can not lookup active pinstate %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drvdata->pstate_suspend = pinctrl_lookup_state(drvdata->pinctrl,
|
||||
"elna_suspend");
|
||||
if (IS_ERR_OR_NULL(drvdata->pstate_suspend)) {
|
||||
ret = PTR_ERR(drvdata->pstate_suspend);
|
||||
pr_err("Can not lookup suspend pinstate %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drvdata->factory_mode = mmi_factory_check();
|
||||
if(drvdata->factory_mode) {
|
||||
ret = pinctrl_select_state(drvdata->pinctrl, drvdata->pstate_active);
|
||||
if(ret) {
|
||||
pr_err("fm_ctrl failed to set pinctrl @factory mode!\n");
|
||||
}
|
||||
else {
|
||||
pr_info("fm_ctrl enable elan @ factory mode\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = pinctrl_select_state(drvdata->pinctrl, drvdata->pstate_default);
|
||||
if(ret) {
|
||||
pr_err("fm_ctrl failed to set pinctrl as default!\n");
|
||||
}
|
||||
else {
|
||||
pr_info("fm_ctrl disable elan as default.\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&dev->kobj, &fm_ctrl_sysfs_attr_grp);
|
||||
if (ret) {
|
||||
pr_err("%s: sysfs group creation failed %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
dev_info(&pdev->dev, "probe: All success !\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fm_ctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct of_device_id fm_ctrl_match[] = {
|
||||
{ .compatible = "moto,fmctrl" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver fm_ctrl_plat_driver = {
|
||||
.probe = fm_ctrl_probe,
|
||||
.remove = fm_ctrl_remove,
|
||||
.driver = {
|
||||
.name = "fm_ctrl",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fm_ctrl_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(fm_ctrl_plat_driver);
|
||||
|
||||
|
||||
MODULE_AUTHOR("Motorola Mobiity");
|
||||
MODULE_DESCRIPTION("FMRadio control interface driver");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
8
drivers/gpio/pcal6408/Android.mk
Executable file
8
drivers/gpio/pcal6408/Android.mk
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := gpio-pcal6408.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
9
drivers/gpio/pcal6408/Kbuild
Executable file
9
drivers/gpio/pcal6408/Kbuild
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
ifeq ($(TARGET_BUILD_VARIANT),userdebug)
|
||||
EXTRA_CFLAGS += -DGPIOI2C_USER_DEBUG
|
||||
endif
|
||||
|
||||
obj-m += gpio-pcal6408.o
|
||||
616
drivers/gpio/pcal6408/gpio-pcal6408.c
Executable file
616
drivers/gpio/pcal6408/gpio-pcal6408.c
Executable file
|
|
@ -0,0 +1,616 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Motorola Mobility, LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#define PCAL6408_DBG(msg, ...) \
|
||||
pr_debug("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__)
|
||||
#define PCAL6408_ERR(msg, ...) \
|
||||
pr_err("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__)
|
||||
#define PCAL6408_LOG(msg, ...) \
|
||||
pr_info("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__)
|
||||
|
||||
#define PCAL6408_ADDR_INPUT 0x00
|
||||
#define PCAL6408_ADDR_OUTPUT 0x01
|
||||
#define PCAL6408_ADDR_CONFIG 0x03
|
||||
#define PCAL6408_ADDR_PULL_EN 0x43
|
||||
#define PCAL6408_ADDR_PULL_CFG 0x44
|
||||
|
||||
#define TCA6418_ADDR_INPUT 0x14
|
||||
#define TCA6418_ADDR_OUTPUT 0x17
|
||||
#define TCA6418_ADDR_CONFIG 0x23
|
||||
#define TCA6418_ADDR_PULL_CFG 0x2C
|
||||
|
||||
#define BIAS_PULL_UP 0
|
||||
#define BIAS_PULL_DOWN 1
|
||||
#define BIAS_NO_PULL 2
|
||||
|
||||
struct pcal6048_dev;
|
||||
|
||||
struct pcal6048_config {
|
||||
char *name;
|
||||
int nr_gpio;
|
||||
int dir_out_val;
|
||||
int direction;
|
||||
int output;
|
||||
int input;
|
||||
int (*get_bit)(int);
|
||||
int (*set_pulldown)(int, struct pcal6048_dev *);
|
||||
int (*set_pullup)(int, struct pcal6048_dev *);
|
||||
int (*set_nopull)(int, struct pcal6048_dev *);
|
||||
int (*get_pull)(int, struct pcal6048_dev *);
|
||||
};
|
||||
|
||||
struct pcal6048_dev {
|
||||
const char *name;
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct gpio_chip gpio_chip;
|
||||
const struct pcal6048_config *conf;
|
||||
struct pinctrl_desc ctrldesc;
|
||||
struct pinctrl_dev *pctl_dev;
|
||||
struct pinctrl_gpio_range grange;
|
||||
int reset_gpio;
|
||||
};
|
||||
|
||||
static int pcal6048_set_reg(struct pcal6048_dev *chip, int gpio, int reg);
|
||||
static int pcal6048_clear_reg(struct pcal6048_dev *chip, int gpio, int reg);
|
||||
static int pcal6048_read_reg(struct pcal6048_dev *chip, int gpio, int reg);
|
||||
|
||||
static int pcal6408_get_bit(int gpio)
|
||||
{
|
||||
return gpio;
|
||||
}
|
||||
|
||||
static int pcal6408_set_pulldown(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_clear_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6408_set_pullup(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6408_set_nopull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_clear_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6408_get_pull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (!pcal6048_read_reg(chip, gpio, PCAL6408_ADDR_PULL_EN))
|
||||
return BIAS_NO_PULL;
|
||||
else if (pcal6048_read_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG))
|
||||
return BIAS_PULL_UP;
|
||||
else
|
||||
return BIAS_PULL_DOWN;
|
||||
}
|
||||
|
||||
static const struct pcal6048_config pcal6408a_conf = {
|
||||
.name = "pcal6408a_conf",
|
||||
.nr_gpio = 8,
|
||||
.set_pulldown = pcal6408_set_pulldown,
|
||||
.set_pullup = pcal6408_set_pullup,
|
||||
.set_nopull = pcal6408_set_nopull,
|
||||
.get_pull = pcal6408_get_pull,
|
||||
.get_bit = pcal6408_get_bit,
|
||||
.dir_out_val = 0, /* Set 0 to direction for output */
|
||||
.direction = PCAL6408_ADDR_CONFIG,
|
||||
.output = PCAL6408_ADDR_OUTPUT,
|
||||
.input = PCAL6408_ADDR_INPUT,
|
||||
};
|
||||
|
||||
static int tca6418_get_bit(int gpio)
|
||||
{
|
||||
/* Bitmapping is strange, gpio7 is bit 0 of first addr,
|
||||
* but gpio8 is bit 0 of second addr, gpio 16 is bit 0
|
||||
* of third addr
|
||||
*/
|
||||
if (gpio <= 7)
|
||||
return (7 - gpio);
|
||||
else if (gpio > 7 && gpio <= 15)
|
||||
return gpio - 8;
|
||||
else
|
||||
return gpio - 16;
|
||||
}
|
||||
|
||||
static int tca6418_set_pulldown(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_clear_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tca6418_set_nopull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_set_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tca6418_get_pull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (!pcal6048_read_reg(chip, gpio, TCA6418_ADDR_PULL_CFG))
|
||||
return BIAS_PULL_DOWN;
|
||||
else
|
||||
return BIAS_NO_PULL;
|
||||
}
|
||||
|
||||
static const struct pcal6048_config tca6418e_conf = {
|
||||
.name = "tca6418e_conf",
|
||||
.nr_gpio = 18,
|
||||
.set_pulldown = tca6418_set_pulldown,
|
||||
.set_pullup = NULL,
|
||||
.set_nopull = tca6418_set_nopull,
|
||||
.get_pull = tca6418_get_pull,
|
||||
.get_bit = tca6418_get_bit,
|
||||
.dir_out_val = 1, /* Set 1 to direction for output */
|
||||
.direction = TCA6418_ADDR_CONFIG,
|
||||
.output = TCA6418_ADDR_OUTPUT,
|
||||
.input = TCA6418_ADDR_INPUT,
|
||||
};
|
||||
|
||||
static int pcal6048_read_reg(struct pcal6048_dev *chip, int gpio, int reg)
|
||||
{
|
||||
int bit_num = chip->conf->get_bit(gpio);
|
||||
uint8_t bit = BIT(bit_num);
|
||||
int reg_off = (gpio / 8);
|
||||
int val;
|
||||
int ret = -1;
|
||||
|
||||
PCAL6408_DBG("%s (gpio %d), read bit %d from 0x%x\n",
|
||||
chip->conf->name, gpio, bit_num, reg + reg_off);
|
||||
|
||||
ret = regmap_read(chip->regmap, reg + reg_off, &val);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return !!(val & bit);
|
||||
}
|
||||
|
||||
static int pcal6048_set_reg(struct pcal6048_dev *chip, int gpio, int reg)
|
||||
{
|
||||
int bit_num = chip->conf->get_bit(gpio);
|
||||
uint8_t bit = BIT(bit_num);
|
||||
int reg_off = (gpio / 8);
|
||||
|
||||
PCAL6408_DBG("%s (gpio %d), set bit %d in 0x%x\n",
|
||||
chip->conf->name, gpio, bit_num, reg + reg_off);
|
||||
|
||||
return regmap_write_bits(chip->regmap, reg + reg_off, bit, bit);
|
||||
}
|
||||
|
||||
static int pcal6048_clear_reg(struct pcal6048_dev *chip, int gpio, int reg)
|
||||
{
|
||||
int bit_num = chip->conf->get_bit(gpio);
|
||||
uint8_t bit = BIT(bit_num);
|
||||
int reg_off = (gpio / 8);
|
||||
|
||||
PCAL6408_DBG("%s (gpio %d), clear bit %d in 0x%x\n",
|
||||
chip->conf->name, gpio, bit_num, reg + reg_off);
|
||||
|
||||
return regmap_write_bits(chip->regmap, reg + reg_off, bit, 0);
|
||||
}
|
||||
|
||||
static int pcal6048_direction_in(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
if (!chip->conf->dir_out_val)
|
||||
ret = pcal6048_set_reg(chip, offset, chip->conf->direction);
|
||||
else
|
||||
ret = pcal6048_clear_reg(chip, offset, chip->conf->direction);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pcal6048_set_gpio(struct gpio_chip *gc, unsigned offset, int value)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
|
||||
if (value)
|
||||
pcal6048_set_reg(chip, offset, chip->conf->output);
|
||||
else
|
||||
pcal6048_clear_reg(chip, offset, chip->conf->output);
|
||||
}
|
||||
|
||||
static int pcal6048_direction_out(struct gpio_chip *gc, unsigned offset, int value)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
ret = pcal6048_set_reg(chip, offset, chip->conf->output);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (chip->conf->dir_out_val)
|
||||
ret = pcal6048_set_reg(chip, offset, chip->conf->direction);
|
||||
else
|
||||
ret = pcal6048_clear_reg(chip, offset, chip->conf->direction);
|
||||
|
||||
pcal6048_set_gpio(gc, offset, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 0 is out, 1 is in */
|
||||
static int pcal6048_get_direction(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
int val = pcal6048_read_reg(chip, offset, chip->conf->direction);
|
||||
|
||||
if (val == chip->conf->dir_out_val)
|
||||
return 0;
|
||||
else;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pcal6048_get_gpio(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
|
||||
/* TODO do I need to read twice to make sure it is cleared? (TCA6418e pg 12) */
|
||||
return pcal6048_read_reg(chip, offset, chip->conf->input);
|
||||
}
|
||||
|
||||
static int pcal6048_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
unsigned arg = pinconf_to_config_argument(config);
|
||||
unsigned param = pinconf_to_config_param(config);
|
||||
|
||||
PCAL6408_DBG("Set config for gpio %d!\n", offset);
|
||||
|
||||
switch (param) {
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
if (chip->conf->set_pullup)
|
||||
return chip->conf->set_pullup(offset, chip);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
if (chip->conf->set_pulldown)
|
||||
return chip->conf->set_pulldown(offset, chip);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
if (chip->conf->set_nopull)
|
||||
return chip->conf->set_nopull(offset, chip);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
case PIN_CONFIG_INPUT_ENABLE:
|
||||
return pcal6048_direction_in(gc, offset);
|
||||
case PIN_CONFIG_OUTPUT_ENABLE:
|
||||
return pcal6048_direction_out(gc, offset, 1);
|
||||
case PIN_CONFIG_OUTPUT:
|
||||
return pcal6048_direction_out(gc, offset, !!arg);
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static void pcal6048_dbg_show(struct seq_file *s, struct gpio_chip *gc)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
unsigned gpio = gc->base;
|
||||
unsigned i;
|
||||
int dir = 0;
|
||||
int pull = 0;
|
||||
|
||||
for (i = 0; i < gc->ngpio; i++, gpio++) {
|
||||
seq_printf(s, " gpio%d:", i);
|
||||
|
||||
dir = pcal6048_get_direction(gc, i);
|
||||
/* Input is 1 */
|
||||
if (dir)
|
||||
seq_printf(s, " in %d", pcal6048_get_gpio(gc, i));
|
||||
else
|
||||
seq_printf(s, " out (set to %d)",
|
||||
pcal6048_read_reg(chip, i, chip->conf->output));
|
||||
|
||||
if (chip->conf->get_pull) {
|
||||
pull = chip->conf->get_pull(i, chip);
|
||||
switch(pull) {
|
||||
case BIAS_NO_PULL:
|
||||
seq_printf(s, " bias-no-pull");
|
||||
break;
|
||||
case BIAS_PULL_DOWN:
|
||||
seq_printf(s, " bias-pull-down");
|
||||
break;
|
||||
case BIAS_PULL_UP:
|
||||
seq_printf(s, " bias-pull-up");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pcal6048_setup_gpio_chip(struct pcal6048_dev *chip)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
|
||||
gc = &chip->gpio_chip;
|
||||
|
||||
gc->label = chip->conf->name;
|
||||
gc->base = -1;
|
||||
gc->ngpio = chip->conf->nr_gpio;
|
||||
gc->parent = chip->dev;
|
||||
gc->owner = THIS_MODULE;
|
||||
gc->direction_input = pcal6048_direction_in;
|
||||
gc->direction_output = pcal6048_direction_out;
|
||||
gc->get_direction = pcal6048_get_direction;
|
||||
gc->get = pcal6048_get_gpio;
|
||||
gc->set = pcal6048_set_gpio;
|
||||
gc->set_config = pcal6048_set_config;
|
||||
gc->dbg_show = pcal6048_dbg_show;
|
||||
gc->can_sleep = true;
|
||||
}
|
||||
|
||||
static int pcal6048_pinconf_get(struct pinctrl_dev *pctldev,
|
||||
unsigned pin, unsigned long *config)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int pcal6048_pinconf_set(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin, unsigned long *configs,
|
||||
unsigned int num_configs)
|
||||
{
|
||||
struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev);
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_configs; i++) {
|
||||
chip->gpio_chip.set_config(&chip->gpio_chip, pin, configs[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6048_get_groups_count(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
return chip->conf->nr_gpio;
|
||||
}
|
||||
|
||||
static const char *pcal6048_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
return chip->conf->name;
|
||||
}
|
||||
|
||||
static const struct pinctrl_ops pcal6048_pctl_ops = {
|
||||
.get_groups_count = pcal6048_get_groups_count,
|
||||
.get_group_name = pcal6048_get_group_name,
|
||||
.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
|
||||
};
|
||||
|
||||
static const struct pinconf_ops pcal6048_pinconf_ops = {
|
||||
.is_generic = true,
|
||||
.pin_config_get = pcal6048_pinconf_get,
|
||||
.pin_config_set = pcal6048_pinconf_set,
|
||||
};
|
||||
|
||||
static int pcal6048_register_pinctrl(struct pcal6048_dev *chip)
|
||||
{
|
||||
struct pinctrl_desc *ctrldesc = &chip->ctrldesc;
|
||||
struct pinctrl_pin_desc *pindesc, *pdesc;
|
||||
int pin;
|
||||
|
||||
ctrldesc->name = chip->conf->name;
|
||||
ctrldesc->owner = THIS_MODULE;
|
||||
ctrldesc->confops = &pcal6048_pinconf_ops;
|
||||
ctrldesc->pctlops = &pcal6048_pctl_ops;
|
||||
|
||||
pindesc = devm_kcalloc(chip->dev,
|
||||
chip->conf->nr_gpio, sizeof(*pindesc),
|
||||
GFP_KERNEL);
|
||||
|
||||
ctrldesc->pins = pindesc;
|
||||
ctrldesc->npins = chip->conf->nr_gpio;
|
||||
|
||||
pdesc = pindesc;
|
||||
for (pin = 0; pin < chip->conf->nr_gpio; pin++) {
|
||||
pdesc->number = pin;
|
||||
pdesc->name = kasprintf(GFP_KERNEL, "gpio%d", pin);
|
||||
pdesc++;
|
||||
}
|
||||
|
||||
chip->pctl_dev = devm_pinctrl_register(chip->dev, ctrldesc, chip);
|
||||
|
||||
if (IS_ERR(chip->pctl_dev)) {
|
||||
PCAL6408_ERR("Failed to register pinctrl\n");
|
||||
return PTR_ERR(chip->pctl_dev);
|
||||
}
|
||||
|
||||
chip->grange.base = chip->gpio_chip.base;
|
||||
chip->grange.npins = chip->conf->nr_gpio;
|
||||
chip->grange.gc = &chip->gpio_chip;
|
||||
|
||||
pinctrl_add_gpio_range(chip->pctl_dev, &chip->grange);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config pcal6048_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xFF,
|
||||
};
|
||||
|
||||
static const struct of_device_id pcal6048_match_table[] = {
|
||||
{ .compatible = "ti,tca6418e", .data = &tca6418e_conf },
|
||||
{ .compatible = "nxp,pcal6408a", .data = &pcal6408a_conf },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int pcal6048_config_reset(struct pcal6048_dev *chip)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
chip->reset_gpio = of_get_gpio(chip->dev->of_node, 0);
|
||||
if (gpio_is_valid(chip->reset_gpio)) {
|
||||
err = devm_gpio_request(chip->dev, chip->reset_gpio, "pcal6048_reset");
|
||||
if (err) {
|
||||
PCAL6408_ERR("Reset gpio request failed\n");
|
||||
return err;
|
||||
} else {
|
||||
/* Active low, so set it high */
|
||||
err = gpio_direction_output(chip->reset_gpio, 1);
|
||||
if (err) {
|
||||
PCAL6408_ERR("Reset gpio set failed\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else
|
||||
PCAL6408_LOG("No reset gpio set in dtb\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6048_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pcal6048_dev *chip;
|
||||
const struct of_device_id *match;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
chip->dev = &client->dev;
|
||||
chip->name = "pcal6048";
|
||||
|
||||
chip->regmap = regmap_init_i2c(client, &pcal6048_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
PCAL6408_ERR("Couldn't initialize register regmap rc = %ld\n",
|
||||
PTR_ERR(chip->regmap));
|
||||
rc = PTR_ERR(chip->regmap);
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
match = of_match_device(pcal6048_match_table, chip->dev);
|
||||
if (!match || !match->data) {
|
||||
PCAL6408_ERR("Missing config data!\n");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
chip->conf = match->data;
|
||||
PCAL6408_DBG("Using config %s\n", chip->conf->name);
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
|
||||
pcal6048_setup_gpio_chip(chip);
|
||||
pcal6048_config_reset(chip);
|
||||
|
||||
rc = devm_gpiochip_add_data(chip->dev, &chip->gpio_chip, chip);
|
||||
if (rc) {
|
||||
PCAL6408_ERR("Couldn't add gpio chip rc=%d\n", rc);
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
pcal6048_register_pinctrl(chip);
|
||||
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
devm_kfree(chip->dev, chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pcal6048_remove(struct i2c_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pcal6048_driver = {
|
||||
.driver = {
|
||||
.name = "pcal6048",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = pcal6048_match_table,
|
||||
},
|
||||
.probe = pcal6048_probe,
|
||||
.remove = pcal6048_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(pcal6048_driver);
|
||||
|
||||
MODULE_DESCRIPTION("pcal6048");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
11
drivers/gud/MobiCoreDriver/Android.mk
Normal file
11
drivers/gud/MobiCoreDriver/Android.mk
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mcDrvModule.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
ifeq ($(RSU_SELECT_INTERNAL_CLOCK), true)
|
||||
KERNEL_CFLAGS += RSU_INTERNAL_CLOCK=y
|
||||
endif
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
49
drivers/gud/MobiCoreDriver/Kbuild
Normal file
49
drivers/gud/MobiCoreDriver/Kbuild
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#/*
|
||||
# * Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
# * All Rights Reserved.
|
||||
# *
|
||||
# * 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.
|
||||
# */
|
||||
#
|
||||
# Makefile for the Kinibi core driver
|
||||
#
|
||||
|
||||
|
||||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -DNDEBUG
|
||||
EXTRA_CFLAGS += -Wno-declaration-after-statement
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/drivers/gud/MobiCoreDriver
|
||||
|
||||
ifneq ($(filter m y,$(RSU_INTERNAL_CLOCK)),)
|
||||
EXTRA_CFLAGS += -DRSU_INTERNAL_CLOCK
|
||||
endif
|
||||
|
||||
obj-m += mcDrvModule.o
|
||||
|
||||
|
||||
mcDrvModule-y := \
|
||||
admin.o \
|
||||
client.o \
|
||||
clientlib.o \
|
||||
clock.o \
|
||||
fastcall.o \
|
||||
iwp.o \
|
||||
logging.o \
|
||||
main.o \
|
||||
mcp.o \
|
||||
mmu.o \
|
||||
nq.o \
|
||||
session.o \
|
||||
teeclientapi.o \
|
||||
user.o \
|
||||
xen_be.o \
|
||||
xen_common.o \
|
||||
xen_fe.o
|
||||
11
drivers/gud/MobiCoreDriver/Makefile
Normal file
11
drivers/gud/MobiCoreDriver/Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
||||
|
||||
1186
drivers/gud/MobiCoreDriver/admin.c
Normal file
1186
drivers/gud/MobiCoreDriver/admin.c
Normal file
File diff suppressed because it is too large
Load diff
33
drivers/gud/MobiCoreDriver/admin.h
Normal file
33
drivers/gud/MobiCoreDriver/admin.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_ADMIN_H_
|
||||
#define _MC_ADMIN_H_
|
||||
|
||||
struct cdev;
|
||||
struct mc_uuid_t;
|
||||
struct tee_object;
|
||||
|
||||
int mc_admin_init(struct cdev *cdev, int (*tee_start_cb)(void),
|
||||
void (*tee_stop_cb)(void));
|
||||
void mc_admin_exit(void);
|
||||
|
||||
struct tee_object *tee_object_select(const struct mc_uuid_t *uuid);
|
||||
struct tee_object *tee_object_get(const struct mc_uuid_t *uuid, bool is_gp);
|
||||
struct tee_object *tee_object_copy(uintptr_t address, size_t length);
|
||||
struct tee_object *tee_object_read(u32 spid, uintptr_t address, size_t length);
|
||||
void tee_object_free(struct tee_object *object);
|
||||
|
||||
#endif /* _MC_ADMIN_H_ */
|
||||
89
drivers/gud/MobiCoreDriver/arm.h
Normal file
89
drivers/gud/MobiCoreDriver/arm.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _MC_ARM_H_
|
||||
#define _MC_ARM_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
static inline bool has_security_extensions(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_secure_mode(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* ARM Trustzone specific masks and modes
|
||||
* Vanilla Linux is unaware of TrustZone extension.
|
||||
* I.e. arch/arm/include/asm/ptrace.h does not define monitor mode.
|
||||
* Also TZ bits in cpuid are not defined, ARM port uses magic numbers,
|
||||
* see arch/arm/kernel/setup.c
|
||||
*/
|
||||
#define ARM_MONITOR_MODE (0x16) /*(0b10110)*/
|
||||
#define ARM_SECURITY_EXTENSION_MASK (0x30)
|
||||
|
||||
/* check if CPU supports the ARM TrustZone Security Extensions */
|
||||
static inline bool has_security_extensions(void)
|
||||
{
|
||||
u32 fea = 0;
|
||||
|
||||
asm volatile(
|
||||
"mrc p15, 0, %[fea], cr0, cr1, 0" :
|
||||
[fea]"=r" (fea));
|
||||
|
||||
mc_dev_devel("CPU Features: 0x%X", fea);
|
||||
|
||||
/*
|
||||
* If the CPU features ID has 0 for security features then the CPU
|
||||
* doesn't support TrustZone at all!
|
||||
*/
|
||||
if ((fea & ARM_SECURITY_EXTENSION_MASK) == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* check if running in secure mode */
|
||||
static inline bool is_secure_mode(void)
|
||||
{
|
||||
u32 cpsr = 0;
|
||||
u32 nsacr = 0;
|
||||
|
||||
asm volatile(
|
||||
"mrc p15, 0, %[nsacr], cr1, cr1, 2\n"
|
||||
"mrs %[cpsr], cpsr\n" :
|
||||
[nsacr]"=r" (nsacr),
|
||||
[cpsr]"=r"(cpsr));
|
||||
|
||||
mc_dev_devel("CPRS.M = set to 0x%X", cpsr & MODE_MASK);
|
||||
mc_dev_devel("SCR.NS = set to 0x%X", nsacr);
|
||||
|
||||
/*
|
||||
* If the NSACR contains the reset value(=0) then most likely we are
|
||||
* running in Secure MODE.
|
||||
* If the cpsr mode is set to monitor mode then we cannot load!
|
||||
*/
|
||||
if (nsacr == 0 || ((cpsr & MODE_MASK) == ARM_MONITOR_MODE))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _MC_ARM_H_ */
|
||||
18
drivers/gud/MobiCoreDriver/build_tag.h
Normal file
18
drivers/gud/MobiCoreDriver/build_tag.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MOBICORE_COMPONENT_BUILD_TAG
|
||||
#define MOBICORE_COMPONENT_BUILD_TAG \
|
||||
"t-base-QC8996-Android-410a-V108-20211010_233907_31049_105441"
|
||||
#endif
|
||||
1486
drivers/gud/MobiCoreDriver/client.c
Normal file
1486
drivers/gud/MobiCoreDriver/client.c
Normal file
File diff suppressed because it is too large
Load diff
126
drivers/gud/MobiCoreDriver/client.h
Normal file
126
drivers/gud/MobiCoreDriver/client.h
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _CLIENT_H_
|
||||
#define _CLIENT_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/sched.h> /* TASK_COMM_LEN */
|
||||
|
||||
#include "mc_user.h" /* many types */
|
||||
|
||||
struct tee_client;
|
||||
struct mcp_open_info;
|
||||
struct tee_mmu;
|
||||
struct interworld_session;
|
||||
|
||||
/* Client */
|
||||
struct tee_client *client_create(bool is_from_kernel);
|
||||
void client_get(struct tee_client *client);
|
||||
int client_put(struct tee_client *client);
|
||||
bool client_has_sessions(struct tee_client *client);
|
||||
void client_close(struct tee_client *client);
|
||||
void client_cleanup(void);
|
||||
|
||||
/* MC */
|
||||
int client_mc_open_session(struct tee_client *client,
|
||||
const struct mc_uuid_t *uuid,
|
||||
uintptr_t tci_va, size_t tci_len, u32 *session_id);
|
||||
int client_mc_open_trustlet(struct tee_client *client,
|
||||
u32 spid, uintptr_t ta_va, size_t ta_len,
|
||||
uintptr_t tci_va, size_t tci_len, u32 *session_id);
|
||||
int client_mc_open_common(struct tee_client *client, struct mcp_open_info *info,
|
||||
u32 *session_id);
|
||||
int client_remove_session(struct tee_client *client, u32 session_id);
|
||||
int client_notify_session(struct tee_client *client, u32 session_id);
|
||||
int client_waitnotif_session(struct tee_client *client, u32 session_id,
|
||||
s32 timeout, bool silent_expiry);
|
||||
int client_get_session_exitcode(struct tee_client *client, u32 session_id,
|
||||
s32 *exit_code);
|
||||
int client_mc_map(struct tee_client *client, u32 session_id,
|
||||
struct tee_mmu *mmu, struct mc_ioctl_buffer *buf);
|
||||
int client_mc_unmap(struct tee_client *client, u32 session_id,
|
||||
const struct mc_ioctl_buffer *buf);
|
||||
|
||||
/* GP */
|
||||
int client_gp_initialize_context(struct tee_client *client,
|
||||
struct gp_return *gp_ret);
|
||||
int client_gp_register_shared_mem(struct tee_client *client,
|
||||
struct tee_mmu *mmu, u32 *sva,
|
||||
const struct gp_shared_memory *memref,
|
||||
struct gp_return *gp_ret);
|
||||
int client_gp_release_shared_mem(struct tee_client *client,
|
||||
const struct gp_shared_memory *memref);
|
||||
int client_gp_open_session(struct tee_client *client,
|
||||
const struct mc_uuid_t *uuid,
|
||||
struct gp_operation *operation,
|
||||
const struct mc_identity *identity,
|
||||
struct gp_return *gp_ret,
|
||||
u32 *session_id);
|
||||
int client_gp_open_session_domu(struct tee_client *client,
|
||||
const struct mc_uuid_t *uuid, u64 started,
|
||||
struct interworld_session *iws,
|
||||
struct tee_mmu **mmus,
|
||||
struct gp_return *gp_ret);
|
||||
int client_gp_close_session(struct tee_client *client, u32 session_id);
|
||||
int client_gp_invoke_command(struct tee_client *client, u32 session_id,
|
||||
u32 command_id,
|
||||
struct gp_operation *operation,
|
||||
struct gp_return *gp_ret);
|
||||
int client_gp_invoke_command_domu(struct tee_client *client, u32 session_id,
|
||||
u64 started, struct interworld_session *iws,
|
||||
struct tee_mmu **mmus,
|
||||
struct gp_return *gp_ret);
|
||||
void client_gp_request_cancellation(struct tee_client *client, u64 started);
|
||||
|
||||
/* Contiguous buffer */
|
||||
int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr,
|
||||
struct vm_area_struct *vmarea);
|
||||
int client_cbuf_free(struct tee_client *client, uintptr_t addr);
|
||||
|
||||
/* GP internal */
|
||||
struct client_gp_operation {
|
||||
struct list_head list;
|
||||
u64 started;
|
||||
u64 slot;
|
||||
int cancelled;
|
||||
};
|
||||
|
||||
/* Called from session when a new operation starts/ends */
|
||||
bool client_gp_operation_add(struct tee_client *client,
|
||||
struct client_gp_operation *operation);
|
||||
void client_gp_operation_remove(struct tee_client *client,
|
||||
struct client_gp_operation *operation);
|
||||
|
||||
/* MMU */
|
||||
struct cbuf;
|
||||
|
||||
struct tee_mmu *client_mmu_create(struct tee_client *client,
|
||||
const struct mc_ioctl_buffer *buf_in,
|
||||
struct cbuf **cbuf_p);
|
||||
void tee_cbuf_put(struct cbuf *cbuf);
|
||||
|
||||
/* Buffer shared with SWd at client level */
|
||||
u32 client_get_cwsm_sva(struct tee_client *client,
|
||||
const struct gp_shared_memory *memref);
|
||||
void client_put_cwsm_sva(struct tee_client *client, u32 sva);
|
||||
|
||||
/* Global */
|
||||
void client_init(void);
|
||||
|
||||
/* Debug */
|
||||
int clients_debug_structs(struct kasnprintf_buf *buf);
|
||||
|
||||
#endif /* _CLIENT_H_ */
|
||||
439
drivers/gud/MobiCoreDriver/clientlib.c
Normal file
439
drivers/gud/MobiCoreDriver/clientlib.c
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "mc_user.h"
|
||||
#include "mc_admin.h"
|
||||
#include "mobicore_driver_api.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "client.h"
|
||||
|
||||
static enum mc_result convert(int err)
|
||||
{
|
||||
switch (-err) {
|
||||
case 0:
|
||||
return MC_DRV_OK;
|
||||
case ENOMSG:
|
||||
return MC_DRV_NO_NOTIFICATION;
|
||||
case EBADMSG:
|
||||
return MC_DRV_ERR_NOTIFICATION;
|
||||
case EAGAIN:
|
||||
return MC_DRV_ERR_OUT_OF_RESOURCES;
|
||||
case EHOSTDOWN:
|
||||
return MC_DRV_ERR_INIT;
|
||||
case ENODEV:
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
case ENXIO:
|
||||
return MC_DRV_ERR_UNKNOWN_SESSION;
|
||||
case EPERM:
|
||||
return MC_DRV_ERR_INVALID_OPERATION;
|
||||
case EBADE:
|
||||
return MC_DRV_ERR_INVALID_RESPONSE;
|
||||
case ETIME:
|
||||
return MC_DRV_ERR_TIMEOUT;
|
||||
case ENOMEM:
|
||||
return MC_DRV_ERR_NO_FREE_MEMORY;
|
||||
case EUCLEAN:
|
||||
return MC_DRV_ERR_FREE_MEMORY_FAILED;
|
||||
case ENOTEMPTY:
|
||||
return MC_DRV_ERR_SESSION_PENDING;
|
||||
case EHOSTUNREACH:
|
||||
return MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||||
case ENOENT:
|
||||
return MC_DRV_ERR_INVALID_DEVICE_FILE;
|
||||
case EINVAL:
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
case EPROTO:
|
||||
return MC_DRV_ERR_KERNEL_MODULE;
|
||||
case ECOMM:
|
||||
return MC_DRV_INFO_NOTIFICATION;
|
||||
case EUNATCH:
|
||||
return MC_DRV_ERR_NQ_FAILED;
|
||||
case ERESTARTSYS:
|
||||
return MC_DRV_ERR_INTERRUPTED_BY_SIGNAL;
|
||||
default:
|
||||
mc_dev_devel("error is %d", err);
|
||||
return MC_DRV_ERR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_valid_device(u32 device_id)
|
||||
{
|
||||
return device_id == MC_DEVICE_ID_DEFAULT;
|
||||
}
|
||||
|
||||
static struct tee_client *client;
|
||||
static int open_count;
|
||||
static DEFINE_MUTEX(dev_mutex); /* Lock for the device */
|
||||
|
||||
static bool clientlib_client_get(void)
|
||||
{
|
||||
int ret = true;
|
||||
|
||||
mutex_lock(&dev_mutex);
|
||||
if (!client)
|
||||
ret = false;
|
||||
else
|
||||
client_get(client);
|
||||
|
||||
mutex_unlock(&dev_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clientlib_client_put(void)
|
||||
{
|
||||
mutex_lock(&dev_mutex);
|
||||
if (client_put(client))
|
||||
client = NULL;
|
||||
|
||||
mutex_unlock(&dev_mutex);
|
||||
}
|
||||
|
||||
enum mc_result mc_open_device(u32 device_id)
|
||||
{
|
||||
enum mc_result mc_result = MC_DRV_OK;
|
||||
int ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!is_valid_device(device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
mutex_lock(&dev_mutex);
|
||||
/* Make sure TEE was started */
|
||||
ret = mc_wait_tee_start();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "TEE failed to start, now or in the past");
|
||||
mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!open_count)
|
||||
client = client_create(true);
|
||||
|
||||
if (client) {
|
||||
open_count++;
|
||||
mc_dev_devel("successfully opened the device");
|
||||
} else {
|
||||
mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
|
||||
mc_dev_err(-ENOMEM, "could not open device");
|
||||
}
|
||||
|
||||
end:
|
||||
mutex_unlock(&dev_mutex);
|
||||
return mc_result;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_open_device);
|
||||
|
||||
enum mc_result mc_close_device(u32 device_id)
|
||||
{
|
||||
enum mc_result mc_result = MC_DRV_OK;
|
||||
|
||||
/* Check parameters */
|
||||
if (!is_valid_device(device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
mutex_lock(&dev_mutex);
|
||||
if (open_count > 1) {
|
||||
open_count--;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check sessions and freeze client */
|
||||
if (client_has_sessions(client)) {
|
||||
mc_result = MC_DRV_ERR_SESSION_PENDING;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Close the device */
|
||||
client_close(client);
|
||||
open_count = 0;
|
||||
|
||||
end:
|
||||
mutex_unlock(&dev_mutex);
|
||||
clientlib_client_put();
|
||||
return mc_result;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_close_device);
|
||||
|
||||
enum mc_result mc_open_session(struct mc_session_handle *session,
|
||||
const struct mc_uuid_t *uuid,
|
||||
u8 *tci_va, u32 tci_len)
|
||||
{
|
||||
enum mc_result ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!session || !uuid)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(
|
||||
client_mc_open_session(client, uuid, (uintptr_t)tci_va, tci_len,
|
||||
&session->session_id));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_open_session);
|
||||
|
||||
enum mc_result mc_open_trustlet(struct mc_session_handle *session, u32 spid,
|
||||
u8 *ta_va, u32 ta_len, u8 *tci_va, u32 tci_len)
|
||||
{
|
||||
enum mc_result ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!session || !ta_va || !ta_len)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(
|
||||
client_mc_open_trustlet(client, spid, (uintptr_t)ta_va, ta_len,
|
||||
(uintptr_t)tci_va, tci_len,
|
||||
&session->session_id));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_open_trustlet);
|
||||
|
||||
enum mc_result mc_close_session(struct mc_session_handle *session)
|
||||
{
|
||||
enum mc_result ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!session)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(client_remove_session(client, session->session_id));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_close_session);
|
||||
|
||||
enum mc_result mc_notify(struct mc_session_handle *session)
|
||||
{
|
||||
enum mc_result ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!session)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(client_notify_session(client, session->session_id));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_notify);
|
||||
|
||||
enum mc_result mc_wait_notification(struct mc_session_handle *session,
|
||||
s32 timeout)
|
||||
{
|
||||
enum mc_result ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!session)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
do {
|
||||
ret = convert(client_waitnotif_session(client,
|
||||
session->session_id,
|
||||
timeout, false));
|
||||
} while ((timeout == MC_INFINITE_TIMEOUT) &&
|
||||
(ret == MC_DRV_ERR_INTERRUPTED_BY_SIGNAL));
|
||||
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_wait_notification);
|
||||
|
||||
enum mc_result mc_malloc_wsm(u32 device_id, u32 align, u32 len, u8 **wsm,
|
||||
u32 wsm_flags)
|
||||
{
|
||||
enum mc_result ret;
|
||||
uintptr_t va;
|
||||
|
||||
/* Check parameters */
|
||||
if (!is_valid_device(device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!len)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!wsm)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(client_cbuf_create(client, len, &va, NULL));
|
||||
if (ret == MC_DRV_OK)
|
||||
*wsm = (u8 *)va;
|
||||
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_malloc_wsm);
|
||||
|
||||
enum mc_result mc_free_wsm(u32 device_id, u8 *wsm)
|
||||
{
|
||||
enum mc_result ret;
|
||||
uintptr_t va = (uintptr_t)wsm;
|
||||
|
||||
/* Check parameters */
|
||||
if (!is_valid_device(device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(client_cbuf_free(client, va));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_free_wsm);
|
||||
|
||||
enum mc_result mc_map(struct mc_session_handle *session, void *address,
|
||||
u32 length, struct mc_bulk_map *map_info)
|
||||
{
|
||||
enum mc_result ret;
|
||||
struct mc_ioctl_buffer buf = {
|
||||
.va = (uintptr_t)address,
|
||||
.len = length,
|
||||
.flags = MC_IO_MAP_INPUT_OUTPUT,
|
||||
};
|
||||
|
||||
/* Check parameters */
|
||||
if (!session)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!map_info)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(client_mc_map(client, session->session_id, NULL, &buf));
|
||||
if (ret == MC_DRV_OK) {
|
||||
map_info->secure_virt_addr = buf.sva;
|
||||
map_info->secure_virt_len = buf.len;
|
||||
}
|
||||
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_map);
|
||||
|
||||
enum mc_result mc_unmap(struct mc_session_handle *session, void *address,
|
||||
struct mc_bulk_map *map_info)
|
||||
{
|
||||
enum mc_result ret;
|
||||
struct mc_ioctl_buffer buf = {
|
||||
.va = (uintptr_t)address,
|
||||
.flags = MC_IO_MAP_INPUT_OUTPUT,
|
||||
};
|
||||
|
||||
/* Check parameters */
|
||||
if (!session)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!map_info)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
buf.len = map_info->secure_virt_len;
|
||||
buf.sva = map_info->secure_virt_addr;
|
||||
|
||||
ret = convert(client_mc_unmap(client, session->session_id, &buf));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_unmap);
|
||||
|
||||
enum mc_result mc_get_session_error_code(struct mc_session_handle *session,
|
||||
s32 *exit_code)
|
||||
{
|
||||
enum mc_result ret;
|
||||
|
||||
/* Check parameters */
|
||||
if (!session)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!is_valid_device(session->device_id))
|
||||
return MC_DRV_ERR_UNKNOWN_DEVICE;
|
||||
|
||||
if (!exit_code)
|
||||
return MC_DRV_ERR_INVALID_PARAMETER;
|
||||
|
||||
if (!clientlib_client_get())
|
||||
return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN;
|
||||
|
||||
/* Call core api */
|
||||
ret = convert(client_get_session_exitcode(client, session->session_id,
|
||||
exit_code));
|
||||
clientlib_client_put();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc_get_session_error_code);
|
||||
202
drivers/gud/MobiCoreDriver/clock.c
Normal file
202
drivers/gud/MobiCoreDriver/clock.c
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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 "platform.h"
|
||||
|
||||
#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "clock.h"
|
||||
|
||||
static struct clk_context {
|
||||
struct clk *mc_ce_iface_clk;
|
||||
struct clk *mc_ce_core_clk;
|
||||
struct clk *mc_ce_bus_clk;
|
||||
struct clk *mc_ce_core_src_clk;
|
||||
/* Clocks are managed by Linux Kernel. No need to do anything */
|
||||
bool no_clock_support;
|
||||
} clk_ctx;
|
||||
|
||||
int mc_clock_init(void)
|
||||
{
|
||||
int ret;
|
||||
#ifdef MC_CLOCK_CORESRC_DEFAULTRATE
|
||||
int core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE;
|
||||
#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME
|
||||
u32 of_core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_node_by_name(NULL, TT_CLOCK_DEVICE_NAME);
|
||||
if (!np) {
|
||||
ret = -ENOENT;
|
||||
mc_dev_err(ret, "cannot get clock device from DT");
|
||||
goto error;
|
||||
}
|
||||
|
||||
clk_ctx.no_clock_support =
|
||||
of_property_read_bool(np, TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE);
|
||||
if (clk_ctx.no_clock_support)
|
||||
return 0;
|
||||
#endif /* TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE */
|
||||
|
||||
#ifdef MC_CLOCK_CORESRC_DEFAULTRATE
|
||||
#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME
|
||||
/* Get core clk src */
|
||||
clk_ctx.mc_ce_core_src_clk = clk_get(g_ctx.mcd, "core_clk_src");
|
||||
if (IS_ERR_OR_NULL(clk_ctx.mc_ce_core_src_clk)) {
|
||||
ret = PTR_ERR(clk_ctx.mc_ce_core_src_clk);
|
||||
mc_dev_err(ret, "cannot get core src clock");
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME
|
||||
ret = of_property_read_u32(g_ctx.mcd->of_node,
|
||||
MC_CRYPTO_CLOCK_CORESRC_PROPNAME,
|
||||
&of_core_src_rate);
|
||||
if (ret) {
|
||||
core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE;
|
||||
mc_dev_info("cannot get clock frequency from DT, use %d",
|
||||
core_src_rate);
|
||||
} else {
|
||||
core_src_rate = of_core_src_rate;
|
||||
}
|
||||
|
||||
#endif /* MC_CRYPTO_CLOCK_CORESRC_PROPNAME */
|
||||
|
||||
ret = clk_set_rate(clk_ctx.mc_ce_core_src_clk, core_src_rate);
|
||||
if (ret) {
|
||||
clk_put(clk_ctx.mc_ce_core_src_clk);
|
||||
clk_ctx.mc_ce_core_src_clk = NULL;
|
||||
mc_dev_err(ret, "cannot set core clock src rate");
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
#endif /* MC_CLOCK_CORESRC_DEFAULTRATE */
|
||||
|
||||
/* Get core clk */
|
||||
clk_ctx.mc_ce_core_clk = clk_get(g_ctx.mcd, "core_clk");
|
||||
if (IS_ERR(clk_ctx.mc_ce_core_clk)) {
|
||||
ret = PTR_ERR(clk_ctx.mc_ce_core_clk);
|
||||
mc_dev_err(ret, "cannot get core clock");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get Interface clk */
|
||||
clk_ctx.mc_ce_iface_clk = clk_get(g_ctx.mcd, "iface_clk");
|
||||
if (IS_ERR(clk_ctx.mc_ce_iface_clk)) {
|
||||
clk_put(clk_ctx.mc_ce_core_clk);
|
||||
ret = PTR_ERR(clk_ctx.mc_ce_iface_clk);
|
||||
mc_dev_err(ret, "cannot get iface clock");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get AXI clk */
|
||||
clk_ctx.mc_ce_bus_clk = clk_get(g_ctx.mcd, "bus_clk");
|
||||
if (IS_ERR(clk_ctx.mc_ce_bus_clk)) {
|
||||
clk_put(clk_ctx.mc_ce_iface_clk);
|
||||
clk_put(clk_ctx.mc_ce_core_clk);
|
||||
ret = PTR_ERR(clk_ctx.mc_ce_bus_clk);
|
||||
mc_dev_err(ret, "cannot get AXI bus clock");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
clk_ctx.mc_ce_core_clk = NULL;
|
||||
clk_ctx.mc_ce_iface_clk = NULL;
|
||||
clk_ctx.mc_ce_bus_clk = NULL;
|
||||
clk_ctx.mc_ce_core_src_clk = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mc_clock_exit(void)
|
||||
{
|
||||
if (clk_ctx.no_clock_support)
|
||||
return;
|
||||
|
||||
if (clk_ctx.mc_ce_iface_clk)
|
||||
clk_put(clk_ctx.mc_ce_iface_clk);
|
||||
|
||||
if (clk_ctx.mc_ce_core_clk)
|
||||
clk_put(clk_ctx.mc_ce_core_clk);
|
||||
|
||||
if (clk_ctx.mc_ce_bus_clk)
|
||||
clk_put(clk_ctx.mc_ce_bus_clk);
|
||||
|
||||
if (clk_ctx.mc_ce_core_src_clk)
|
||||
clk_put(clk_ctx.mc_ce_core_src_clk);
|
||||
}
|
||||
|
||||
int mc_clock_enable(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (clk_ctx.no_clock_support)
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(clk_ctx.mc_ce_core_clk);
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "cannot enable core clock");
|
||||
goto err_core;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_ctx.mc_ce_iface_clk);
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "cannot enable interface clock");
|
||||
goto err_iface;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_ctx.mc_ce_bus_clk);
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "cannot enable bus clock");
|
||||
goto err_bus;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_bus:
|
||||
clk_disable_unprepare(clk_ctx.mc_ce_iface_clk);
|
||||
err_iface:
|
||||
clk_disable_unprepare(clk_ctx.mc_ce_core_clk);
|
||||
err_core:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mc_clock_disable(void)
|
||||
{
|
||||
if (clk_ctx.no_clock_support)
|
||||
return;
|
||||
|
||||
if (clk_ctx.mc_ce_iface_clk)
|
||||
clk_disable_unprepare(clk_ctx.mc_ce_iface_clk);
|
||||
|
||||
if (clk_ctx.mc_ce_core_clk)
|
||||
clk_disable_unprepare(clk_ctx.mc_ce_core_clk);
|
||||
|
||||
if (clk_ctx.mc_ce_bus_clk)
|
||||
clk_disable_unprepare(clk_ctx.mc_ce_bus_clk);
|
||||
}
|
||||
|
||||
#endif /* MC_CRYPTO_CLOCK_MANAGEMENT */
|
||||
54
drivers/gud/MobiCoreDriver/clock.h
Normal file
54
drivers/gud/MobiCoreDriver/clock.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_CLOCK_H_
|
||||
#define _MC_CLOCK_H_
|
||||
|
||||
#include "platform.h" /* MC_CRYPTO_CLOCK_MANAGEMENT */
|
||||
|
||||
#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
|
||||
|
||||
/* Initialize secure crypto clocks */
|
||||
int mc_clock_init(void);
|
||||
/* Free secure crypto clocks */
|
||||
void mc_clock_exit(void);
|
||||
/* Enable secure crypto clocks */
|
||||
int mc_clock_enable(void);
|
||||
/* Disable secure crypto clocks */
|
||||
void mc_clock_disable(void);
|
||||
|
||||
#else /* MC_CRYPTO_CLOCK_MANAGEMENT */
|
||||
|
||||
static inline int mc_clock_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mc_clock_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mc_clock_enable(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mc_clock_disable(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !MC_CRYPTO_CLOCK_MANAGEMENT */
|
||||
|
||||
#endif /* _MC_CLOCK_H_ */
|
||||
379
drivers/gud/MobiCoreDriver/fastcall.c
Normal file
379
drivers/gud/MobiCoreDriver/fastcall.c
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/debugfs.h>
|
||||
#include <linux/sched.h> /* local_clock */
|
||||
#include <linux/version.h>
|
||||
#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
|
||||
#include <linux/sched/clock.h> /* local_clock */
|
||||
#endif
|
||||
|
||||
#include "mcifc.h"
|
||||
|
||||
#include "platform.h" /* MC_SMC_FASTCALL */
|
||||
#include "main.h"
|
||||
#include "fastcall.h"
|
||||
|
||||
/* Use the arch_extension sec pseudo op before switching to secure world */
|
||||
#if defined(__GNUC__) && \
|
||||
defined(__GNUC_MINOR__) && \
|
||||
defined(__GNUC_PATCHLEVEL__) && \
|
||||
((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)) \
|
||||
>= 40502
|
||||
#ifndef CONFIG_ARM64
|
||||
#define MC_ARCH_EXTENSION_SEC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Base for all fastcalls, do not use outside of other structs */
|
||||
union fc_common {
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 param[3];
|
||||
} in;
|
||||
|
||||
struct {
|
||||
u32 resp;
|
||||
u32 ret;
|
||||
u32 param[2];
|
||||
} out;
|
||||
};
|
||||
|
||||
union fc_init {
|
||||
union fc_common common;
|
||||
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 base;
|
||||
u32 nq_info;
|
||||
u32 mcp_info;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
u32 resp;
|
||||
u32 ret;
|
||||
u32 flags;
|
||||
u32 rfu;
|
||||
} out;
|
||||
};
|
||||
|
||||
union fc_info {
|
||||
union fc_common common;
|
||||
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 ext_info_id;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
u32 resp;
|
||||
u32 ret;
|
||||
u32 state;
|
||||
u32 ext_info;
|
||||
} out;
|
||||
};
|
||||
|
||||
union fc_trace {
|
||||
union fc_common common;
|
||||
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 buffer_low;
|
||||
u32 buffer_high;
|
||||
u32 size;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
u32 resp;
|
||||
u32 ret;
|
||||
} out;
|
||||
};
|
||||
|
||||
union fc_nsiq {
|
||||
union fc_common common;
|
||||
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 debug_ret;
|
||||
u32 debug_session_id;
|
||||
u32 debug_payload;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
u32 resp;
|
||||
u32 ret;
|
||||
} out;
|
||||
};
|
||||
|
||||
union fc_yield {
|
||||
union fc_common common;
|
||||
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 debug_ret;
|
||||
u32 debug_timeslice;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
u32 resp;
|
||||
u32 ret;
|
||||
} out;
|
||||
};
|
||||
|
||||
/* Structure to log SMC calls */
|
||||
struct smc_log_entry {
|
||||
u64 cpu_clk;
|
||||
int cpu_id;
|
||||
union fc_common fc;
|
||||
};
|
||||
|
||||
#define SMC_LOG_SIZE 1024
|
||||
static struct smc_log_entry smc_log[SMC_LOG_SIZE];
|
||||
static int smc_log_index;
|
||||
|
||||
/*
|
||||
* convert fast call return code to linux driver module error code
|
||||
*/
|
||||
static int convert_fc_ret(u32 ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case MC_FC_RET_OK:
|
||||
return 0;
|
||||
case MC_FC_RET_ERR_INVALID:
|
||||
return -EINVAL;
|
||||
case MC_FC_RET_ERR_ALREADY_INITIALIZED:
|
||||
return -EBUSY;
|
||||
default:
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* __smc() - fast call to MobiCore
|
||||
*
|
||||
* @data: pointer to fast call data
|
||||
*/
|
||||
static inline int __smc(union fc_common *fc, const char *func)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Log SMC call */
|
||||
smc_log[smc_log_index].cpu_clk = local_clock();
|
||||
smc_log[smc_log_index].cpu_id = raw_smp_processor_id();
|
||||
smc_log[smc_log_index].fc = *fc;
|
||||
if (++smc_log_index >= SMC_LOG_SIZE)
|
||||
smc_log_index = 0;
|
||||
|
||||
#ifdef MC_SMC_FASTCALL
|
||||
ret = smc_fastcall(fc, sizeof(*fc));
|
||||
#else /* MC_SMC_FASTCALL */
|
||||
{
|
||||
#ifdef CONFIG_ARM64
|
||||
/* SMC expect values in x0-x3 */
|
||||
register u64 reg0 __asm__("x0") = fc->in.cmd;
|
||||
register u64 reg1 __asm__("x1") = fc->in.param[0];
|
||||
register u64 reg2 __asm__("x2") = fc->in.param[1];
|
||||
register u64 reg3 __asm__("x3") = fc->in.param[2];
|
||||
|
||||
/*
|
||||
* According to AARCH64 SMC Calling Convention (ARM DEN 0028A),
|
||||
* section 3.1: registers x4-x17 are unpredictable/scratch
|
||||
* registers. So we have to make sure that the compiler does
|
||||
* not allocate any of those registers by letting him know that
|
||||
* the asm code might clobber them.
|
||||
*/
|
||||
__asm__ volatile (
|
||||
"smc #0\n"
|
||||
: "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3)
|
||||
:
|
||||
: "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11",
|
||||
"x12", "x13", "x14", "x15", "x16", "x17"
|
||||
);
|
||||
#else /* CONFIG_ARM64 */
|
||||
/* SMC expect values in r0-r3 */
|
||||
register u32 reg0 __asm__("r0") = fc->in.cmd;
|
||||
register u32 reg1 __asm__("r1") = fc->in.param[0];
|
||||
register u32 reg2 __asm__("r2") = fc->in.param[1];
|
||||
register u32 reg3 __asm__("r3") = fc->in.param[2];
|
||||
|
||||
__asm__ volatile (
|
||||
#ifdef MC_ARCH_EXTENSION_SEC
|
||||
/*
|
||||
* This pseudo op is supported and required from
|
||||
* binutils 2.21 on
|
||||
*/
|
||||
".arch_extension sec\n"
|
||||
#endif /* MC_ARCH_EXTENSION_SEC */
|
||||
"smc #0\n"
|
||||
: "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3)
|
||||
);
|
||||
|
||||
#endif /* !CONFIG_ARM64 */
|
||||
|
||||
/* set response */
|
||||
fc->out.resp = reg0;
|
||||
fc->out.ret = reg1;
|
||||
fc->out.param[0] = reg2;
|
||||
fc->out.param[1] = reg3;
|
||||
}
|
||||
#endif /* !MC_SMC_FASTCALL */
|
||||
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "failed for %s", func);
|
||||
} else {
|
||||
ret = convert_fc_ret(fc->out.ret);
|
||||
if (ret)
|
||||
mc_dev_err(ret, "%s failed (%x)", func, fc->out.ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define smc(__fc__) __smc(__fc__.common, __func__)
|
||||
|
||||
int fc_init(uintptr_t addr, ptrdiff_t off, size_t q_len, size_t buf_len)
|
||||
{
|
||||
union fc_init fc;
|
||||
#ifdef CONFIG_ARM64
|
||||
u32 addr_high = (u32)(addr >> 32);
|
||||
#else
|
||||
u32 addr_high = 0;
|
||||
#endif
|
||||
|
||||
/* Call the INIT fastcall to setup MobiCore initialization */
|
||||
memset(&fc, 0, sizeof(fc));
|
||||
fc.in.cmd = MC_FC_INIT;
|
||||
/* base address of mci buffer PAGE_SIZE (default is 4KB) aligned */
|
||||
fc.in.base = (u32)addr;
|
||||
/* notification buffer start/length [16:16] [start, length] */
|
||||
fc.in.nq_info = (u32)(((addr_high & 0xFFFF) << 16) | (q_len & 0xFFFF));
|
||||
/* mcp buffer start/length [16:16] [start, length] */
|
||||
fc.in.mcp_info = (u32)((off << 16) | (buf_len & 0xFFFF));
|
||||
mc_dev_devel("cmd=0x%08x, base=0x%08x, nq_info=0x%08x, mcp_info=0x%08x",
|
||||
fc.in.cmd, fc.in.base, fc.in.nq_info,
|
||||
fc.in.mcp_info);
|
||||
return smc(&fc);
|
||||
}
|
||||
|
||||
int fc_info(u32 ext_info_id, u32 *state, u32 *ext_info)
|
||||
{
|
||||
union fc_info fc;
|
||||
int ret = 0;
|
||||
|
||||
memset(&fc, 0, sizeof(fc));
|
||||
fc.in.cmd = MC_FC_INFO;
|
||||
fc.in.ext_info_id = ext_info_id;
|
||||
ret = smc(&fc);
|
||||
if (ret) {
|
||||
if (state)
|
||||
*state = MC_STATUS_NOT_INITIALIZED;
|
||||
|
||||
if (ext_info)
|
||||
*ext_info = 0;
|
||||
|
||||
mc_dev_err(ret, "failed for index %d", ext_info_id);
|
||||
} else {
|
||||
if (state)
|
||||
*state = fc.out.state;
|
||||
|
||||
if (ext_info)
|
||||
*ext_info = fc.out.ext_info;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fc_trace_init(phys_addr_t buffer, u32 size)
|
||||
{
|
||||
union fc_trace fc;
|
||||
|
||||
memset(&fc, 0, sizeof(fc));
|
||||
fc.in.cmd = MC_FC_MEM_TRACE;
|
||||
fc.in.buffer_low = (u32)buffer;
|
||||
#ifdef CONFIG_ARM64
|
||||
fc.in.buffer_high = (u32)(buffer >> 32);
|
||||
#endif
|
||||
fc.in.size = size;
|
||||
return smc(&fc);
|
||||
}
|
||||
|
||||
int fc_trace_deinit(void)
|
||||
{
|
||||
return fc_trace_init(0, 0);
|
||||
}
|
||||
|
||||
/* sid, payload only used for debug purpose */
|
||||
int fc_nsiq(u32 session_id, u32 payload)
|
||||
{
|
||||
union fc_nsiq fc;
|
||||
|
||||
memset(&fc, 0, sizeof(fc));
|
||||
fc.in.cmd = MC_SMC_N_SIQ;
|
||||
fc.in.debug_session_id = session_id;
|
||||
fc.in.debug_payload = payload;
|
||||
return smc(&fc);
|
||||
}
|
||||
|
||||
/* timeslice only used for debug purpose */
|
||||
int fc_yield(u32 timeslice)
|
||||
{
|
||||
union fc_yield fc;
|
||||
|
||||
memset(&fc, 0, sizeof(fc));
|
||||
fc.in.cmd = MC_SMC_N_YIELD;
|
||||
fc.in.debug_timeslice = timeslice;
|
||||
return smc(&fc);
|
||||
}
|
||||
|
||||
static int show_smc_log_entry(struct kasnprintf_buf *buf,
|
||||
struct smc_log_entry *entry)
|
||||
{
|
||||
return kasnprintf(buf, "%20llu %10d 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
||||
entry->cpu_clk, entry->cpu_id, entry->fc.in.cmd,
|
||||
entry->fc.in.param[0], entry->fc.in.param[1],
|
||||
entry->fc.in.param[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump SMC log circular buffer, starting from oldest command. It is assumed
|
||||
* nothing goes in any more at this point.
|
||||
*/
|
||||
int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
ret = kasnprintf(buf, "%10s %20s %10s %-10s %-10s %-10s\n", "CPU id",
|
||||
"CPU clock", "command", "param1", "param2", "param3");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (smc_log[smc_log_index].cpu_clk)
|
||||
/* Buffer has wrapped around, dump end (oldest records) */
|
||||
for (i = smc_log_index; i < SMC_LOG_SIZE; i++) {
|
||||
ret = show_smc_log_entry(buf, &smc_log[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Dump first records */
|
||||
for (i = 0; i < smc_log_index; i++) {
|
||||
ret = show_smc_log_entry(buf, &smc_log[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
28
drivers/gud/MobiCoreDriver/fastcall.h
Normal file
28
drivers/gud/MobiCoreDriver/fastcall.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TBASE_FASTCALL_H_
|
||||
#define _TBASE_FASTCALL_H_
|
||||
|
||||
int fc_init(uintptr_t base_pa, ptrdiff_t off, size_t q_len, size_t buf_len);
|
||||
int fc_info(u32 ext_info_id, u32 *state, u32 *ext_info);
|
||||
int fc_trace_init(phys_addr_t buffer, u32 size);
|
||||
int fc_trace_deinit(void);
|
||||
int fc_nsiq(u32 session_id, u32 payload);
|
||||
int fc_yield(u32 timeslice);
|
||||
|
||||
int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf);
|
||||
|
||||
#endif /* _TBASE_FASTCALL_H_ */
|
||||
69
drivers/gud/MobiCoreDriver/gptci.h
Normal file
69
drivers/gud/MobiCoreDriver/gptci.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _GP_TCI_H_
|
||||
#define _GP_TCI_H_
|
||||
|
||||
struct tee_value {
|
||||
u32 a;
|
||||
u32 b;
|
||||
};
|
||||
|
||||
struct _teec_memory_reference_internal {
|
||||
u32 sva;
|
||||
u32 len;
|
||||
u32 output_size;
|
||||
};
|
||||
|
||||
union _teec_parameter_internal {
|
||||
struct tee_value value;
|
||||
struct _teec_memory_reference_internal memref;
|
||||
};
|
||||
|
||||
enum _teec_tci_type {
|
||||
_TA_OPERATION_OPEN_SESSION = 1,
|
||||
_TA_OPERATION_INVOKE_COMMAND = 2,
|
||||
_TA_OPERATION_CLOSE_SESSION = 3,
|
||||
};
|
||||
|
||||
struct _teec_operation_internal {
|
||||
enum _teec_tci_type type;
|
||||
u32 command_id;
|
||||
u32 param_types;
|
||||
union _teec_parameter_internal params[4];
|
||||
bool is_cancelled;
|
||||
u8 rfu_padding[3];
|
||||
};
|
||||
|
||||
struct _teec_tci {
|
||||
char header[8];
|
||||
struct teec_uuid destination;
|
||||
struct _teec_operation_internal operation;
|
||||
u32 ready;
|
||||
u32 return_origin;
|
||||
u32 return_status;
|
||||
};
|
||||
|
||||
/**
|
||||
* Termination codes
|
||||
*/
|
||||
#define TA_EXIT_CODE_PANIC 300
|
||||
#define TA_EXIT_CODE_TCI 301
|
||||
#define TA_EXIT_CODE_PARAMS 302
|
||||
#define TA_EXIT_CODE_FINISHED 303
|
||||
#define TA_EXIT_CODE_SESSIONSTATE 304
|
||||
#define TA_EXIT_CODE_CREATEFAILED 305
|
||||
|
||||
#endif /* _GP_TCI_H_ */
|
||||
1218
drivers/gud/MobiCoreDriver/iwp.c
Normal file
1218
drivers/gud/MobiCoreDriver/iwp.c
Normal file
File diff suppressed because it is too large
Load diff
124
drivers/gud/MobiCoreDriver/iwp.h
Normal file
124
drivers/gud/MobiCoreDriver/iwp.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_IWP_H_
|
||||
#define _MC_IWP_H_
|
||||
|
||||
#include "mcloadformat.h" /* struct identity */
|
||||
|
||||
#include "nq.h"
|
||||
#include "mcp.h" /* mcp_buffer_map FIXME move to nq? */
|
||||
|
||||
struct iwp_session {
|
||||
/* Notification queue session */
|
||||
struct nq_session nq_session;
|
||||
/* Session ID */
|
||||
u32 sid;
|
||||
/* IWS slot */
|
||||
u64 slot;
|
||||
/* IWS other slot needed at open */
|
||||
u64 op_slot;
|
||||
/* Sessions list (protected by iwp sessions_lock) */
|
||||
struct list_head list;
|
||||
/* Notification waiter lock */
|
||||
struct mutex notif_wait_lock; /* Only one at a time */
|
||||
/* Notification received */
|
||||
struct completion completion;
|
||||
/* Interworld struct lock */
|
||||
struct mutex iws_lock;
|
||||
/* Session state (protected by iwp sessions_lock) */
|
||||
enum iwp_session_state {
|
||||
IWP_SESSION_RUNNING,
|
||||
IWP_SESSION_CLOSE_REQUESTED,
|
||||
IWP_SESSION_CLOSED,
|
||||
} state;
|
||||
/* GP TAs have login information */
|
||||
struct identity client_identity;
|
||||
};
|
||||
|
||||
struct iwp_buffer_map {
|
||||
struct mcp_buffer_map map;
|
||||
u32 sva;
|
||||
};
|
||||
|
||||
/* Private to iwp_session structure */
|
||||
void iwp_session_init(struct iwp_session *session,
|
||||
const struct identity *identity);
|
||||
|
||||
/* Getters */
|
||||
static inline u32 iwp_session_id(struct iwp_session *session)
|
||||
{
|
||||
return session->sid;
|
||||
}
|
||||
|
||||
static inline u64 iwp_session_slot(struct iwp_session *session)
|
||||
{
|
||||
return session->slot;
|
||||
}
|
||||
|
||||
/* Convert local errno to GP return values */
|
||||
int iwp_set_ret(int ret, struct gp_return *gp_ret);
|
||||
|
||||
/* Commands */
|
||||
int iwp_register_shared_mem(
|
||||
struct tee_mmu *mmu,
|
||||
u32 *sva,
|
||||
struct gp_return *gp_ret);
|
||||
int iwp_release_shared_mem(
|
||||
struct mcp_buffer_map *map);
|
||||
int iwp_open_session_prepare(
|
||||
struct iwp_session *session,
|
||||
struct gp_operation *operation,
|
||||
struct mc_ioctl_buffer *bufs,
|
||||
struct gp_shared_memory **parents,
|
||||
struct gp_return *gp_ret);
|
||||
void iwp_open_session_abort(
|
||||
struct iwp_session *iwp_session);
|
||||
int iwp_open_session(
|
||||
struct iwp_session *iwp_session,
|
||||
const struct mc_uuid_t *uuid,
|
||||
struct gp_operation *operation,
|
||||
const struct iwp_buffer_map *maps,
|
||||
struct interworld_session *iws,
|
||||
struct tee_mmu **mmus,
|
||||
struct gp_return *gp_ret);
|
||||
int iwp_close_session(
|
||||
struct iwp_session *iwp_session);
|
||||
int iwp_invoke_command_prepare(
|
||||
struct iwp_session *iwp_session,
|
||||
u32 command_id,
|
||||
struct gp_operation *operation,
|
||||
struct mc_ioctl_buffer *bufs,
|
||||
struct gp_shared_memory **parents,
|
||||
struct gp_return *gp_ret);
|
||||
void iwp_invoke_command_abort(
|
||||
struct iwp_session *iwp_session);
|
||||
int iwp_invoke_command(
|
||||
struct iwp_session *iwp_session,
|
||||
struct gp_operation *operation,
|
||||
const struct iwp_buffer_map *maps,
|
||||
struct interworld_session *iws,
|
||||
struct tee_mmu **mmus,
|
||||
struct gp_return *gp_ret);
|
||||
int iwp_request_cancellation(
|
||||
u64 slot);
|
||||
|
||||
/* Initialisation/cleanup */
|
||||
int iwp_init(void);
|
||||
void iwp_exit(void);
|
||||
int iwp_start(void);
|
||||
void iwp_stop(void);
|
||||
|
||||
#endif /* _MC_IWP_H_ */
|
||||
261
drivers/gud/MobiCoreDriver/logging.c
Normal file
261
drivers/gud/MobiCoreDriver/logging.c
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* Supported log buffer version */
|
||||
#define MC_LOG_VERSION 2
|
||||
|
||||
/* Default length of the log ring buffer 256KiB */
|
||||
#define LOG_BUF_ORDER 6
|
||||
|
||||
/* Max Len of a log line for printing */
|
||||
#define LOG_LINE_SIZE 256
|
||||
|
||||
/* Definitions for log version 2 */
|
||||
#define LOG_TYPE_MASK (0x0007)
|
||||
#define LOG_TYPE_CHAR 0
|
||||
#define LOG_TYPE_INTEGER 1
|
||||
|
||||
/* Field length */
|
||||
#define LOG_LENGTH_MASK (0x00F8)
|
||||
#define LOG_LENGTH_SHIFT 3
|
||||
|
||||
/* Extra attributes */
|
||||
#define LOG_EOL (0x0100)
|
||||
#define LOG_INTEGER_DECIMAL (0x0200)
|
||||
#define LOG_INTEGER_SIGNED (0x0400)
|
||||
|
||||
/* active cpu id */
|
||||
#define LOG_CPUID_MASK (0xF000)
|
||||
#define LOG_CPUID_SHIFT 12
|
||||
|
||||
struct mc_logmsg {
|
||||
u16 ctrl; /* Type and format of data */
|
||||
u16 source; /* Unique value for each event source */
|
||||
u32 log_data; /* Value, if any */
|
||||
};
|
||||
|
||||
/* MobiCore internal trace buffer structure. */
|
||||
struct mc_trace_buf {
|
||||
u32 version; /* version of trace buffer */
|
||||
u32 length; /* length of buff */
|
||||
u32 head; /* last write position */
|
||||
u8 buff[]; /* start of the log buffer */
|
||||
};
|
||||
|
||||
static struct logging_ctx {
|
||||
struct kthread_work work;
|
||||
struct kthread_worker worker;
|
||||
struct task_struct *thread;
|
||||
union {
|
||||
struct mc_trace_buf *trace_buf; /* Circular log buffer */
|
||||
unsigned long trace_page;
|
||||
};
|
||||
u32 tail; /* MobiCore log read position */
|
||||
int thread_err;
|
||||
u16 prev_source; /* Previous Log source */
|
||||
char line[LOG_LINE_SIZE + 1];/* Log Line buffer */
|
||||
u32 line_len; /* Log Line buffer current length */
|
||||
#if KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE
|
||||
u32 enabled; /* Log can be disabled via debugfs */
|
||||
#else
|
||||
bool enabled; /* Log can be disabled via debugfs */
|
||||
#endif
|
||||
bool dead;
|
||||
} log_ctx;
|
||||
|
||||
static inline void log_eol(u16 source, u32 cpuid)
|
||||
{
|
||||
if (!log_ctx.line_len)
|
||||
return;
|
||||
|
||||
if (log_ctx.prev_source)
|
||||
/* TEE user-space */
|
||||
dev_info(g_ctx.mcd, "%03x(%u)|%s\n", log_ctx.prev_source,
|
||||
cpuid, log_ctx.line);
|
||||
else
|
||||
/* TEE kernel */
|
||||
dev_info(g_ctx.mcd, "mtk(%u)|%s\n", cpuid, log_ctx.line);
|
||||
log_ctx.line[0] = '\0';
|
||||
log_ctx.line_len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Collect chars in log_ctx.line buffer and output the buffer when it is full.
|
||||
* No locking needed because only "mobicore_log" thread updates this buffer.
|
||||
*/
|
||||
static inline void log_char(char ch, u16 source, u32 cpuid)
|
||||
{
|
||||
if (ch == '\0')
|
||||
return;
|
||||
|
||||
if (ch == '\n' || ch == '\r') {
|
||||
log_eol(source, cpuid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_ctx.line_len >= LOG_LINE_SIZE || source != log_ctx.prev_source)
|
||||
log_eol(source, cpuid);
|
||||
|
||||
log_ctx.line[log_ctx.line_len++] = ch;
|
||||
log_ctx.line[log_ctx.line_len] = 0;
|
||||
log_ctx.prev_source = source;
|
||||
}
|
||||
|
||||
static inline void log_string(u32 ch, u16 source, u32 cpuid)
|
||||
{
|
||||
while (ch) {
|
||||
log_char(ch & 0xFF, source, cpuid);
|
||||
ch >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void log_number(u32 format, u32 value, u16 source, u32 cpuid)
|
||||
{
|
||||
int width = (format & LOG_LENGTH_MASK) >> LOG_LENGTH_SHIFT;
|
||||
char fmt[16];
|
||||
char buffer[32];
|
||||
const char *reader = buffer;
|
||||
|
||||
if (format & LOG_INTEGER_DECIMAL)
|
||||
if (format & LOG_INTEGER_SIGNED)
|
||||
snprintf(fmt, sizeof(fmt), "%%%ud", width);
|
||||
else
|
||||
snprintf(fmt, sizeof(fmt), "%%%uu", width);
|
||||
else
|
||||
snprintf(fmt, sizeof(fmt), "%%0%ux", width);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), fmt, value);
|
||||
while (*reader)
|
||||
log_char(*reader++, source, cpuid);
|
||||
}
|
||||
|
||||
static inline int log_msg(void *data)
|
||||
{
|
||||
struct mc_logmsg *msg = (struct mc_logmsg *)data;
|
||||
int log_type = msg->ctrl & LOG_TYPE_MASK;
|
||||
int cpuid = ((msg->ctrl & LOG_CPUID_MASK) >> LOG_CPUID_SHIFT);
|
||||
|
||||
switch (log_type) {
|
||||
case LOG_TYPE_CHAR:
|
||||
log_string(msg->log_data, msg->source, cpuid);
|
||||
break;
|
||||
case LOG_TYPE_INTEGER:
|
||||
log_number(msg->ctrl, msg->log_data, msg->source, cpuid);
|
||||
break;
|
||||
}
|
||||
if (msg->ctrl & LOG_EOL)
|
||||
log_eol(msg->source, cpuid);
|
||||
|
||||
return sizeof(*msg);
|
||||
}
|
||||
|
||||
static void logging_worker(struct kthread_work *work)
|
||||
{
|
||||
static DEFINE_MUTEX(local_mutex);
|
||||
|
||||
mutex_lock(&local_mutex);
|
||||
while (log_ctx.trace_buf->head != log_ctx.tail) {
|
||||
if (log_ctx.trace_buf->version != MC_LOG_VERSION) {
|
||||
mc_dev_err(-EINVAL, "Bad log data v%d (exp. v%d), stop",
|
||||
log_ctx.trace_buf->version, MC_LOG_VERSION);
|
||||
log_ctx.dead = true;
|
||||
break;
|
||||
}
|
||||
|
||||
log_ctx.tail += log_msg(&log_ctx.trace_buf->buff[log_ctx.tail]);
|
||||
/* Wrap over if no space left for a complete message */
|
||||
if ((log_ctx.tail + sizeof(struct mc_logmsg)) >
|
||||
log_ctx.trace_buf->length)
|
||||
log_ctx.tail = 0;
|
||||
}
|
||||
mutex_unlock(&local_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wake up the log reader thread
|
||||
* This should be called from the places where calls into MobiCore have
|
||||
* generated some logs(eg, yield, SIQ...)
|
||||
*/
|
||||
void logging_run(void)
|
||||
{
|
||||
if (log_ctx.enabled && !log_ctx.dead &&
|
||||
log_ctx.trace_buf->head != log_ctx.tail)
|
||||
#if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE
|
||||
queue_kthread_work(&log_ctx.worker, &log_ctx.work);
|
||||
#else
|
||||
kthread_queue_work(&log_ctx.worker, &log_ctx.work);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup MobiCore kernel log. It assumes it's running on CORE 0!
|
||||
* The fastcall will complain if that is not the case!
|
||||
*/
|
||||
int logging_init(phys_addr_t *buffer, u32 *size)
|
||||
{
|
||||
/*
|
||||
* We are going to map this buffer into virtual address space in SWd.
|
||||
* To reduce complexity there, we use a contiguous buffer.
|
||||
*/
|
||||
log_ctx.trace_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
||||
LOG_BUF_ORDER);
|
||||
if (!log_ctx.trace_page)
|
||||
return -ENOMEM;
|
||||
|
||||
*buffer = virt_to_phys((void *)(log_ctx.trace_page));
|
||||
*size = BIT(LOG_BUF_ORDER) * PAGE_SIZE;
|
||||
|
||||
/* Logging thread */
|
||||
#if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE
|
||||
init_kthread_work(&log_ctx.work, logging_worker);
|
||||
init_kthread_worker(&log_ctx.worker);
|
||||
#else
|
||||
kthread_init_work(&log_ctx.work, logging_worker);
|
||||
kthread_init_worker(&log_ctx.worker);
|
||||
#endif
|
||||
log_ctx.thread = kthread_create(kthread_worker_fn, &log_ctx.worker,
|
||||
"tee_log");
|
||||
if (IS_ERR(log_ctx.thread))
|
||||
return PTR_ERR(log_ctx.thread);
|
||||
|
||||
wake_up_process(log_ctx.thread);
|
||||
|
||||
/* Debugfs switch */
|
||||
log_ctx.enabled = true;
|
||||
debugfs_create_bool("swd_debug", 0600, g_ctx.debug_dir,
|
||||
&log_ctx.enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void logging_exit(bool buffer_busy)
|
||||
{
|
||||
/*
|
||||
* This is not racey as the only caller for logging_run is the
|
||||
* scheduler which gets stopped before us, and long before we exit.
|
||||
*/
|
||||
kthread_stop(log_ctx.thread);
|
||||
if (!buffer_busy)
|
||||
free_pages(log_ctx.trace_page, LOG_BUF_ORDER);
|
||||
}
|
||||
23
drivers/gud/MobiCoreDriver/logging.h
Normal file
23
drivers/gud/MobiCoreDriver/logging.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_LOGGING_H_
|
||||
#define _MC_LOGGING_H_
|
||||
|
||||
void logging_run(void);
|
||||
int logging_init(phys_addr_t *buffer, u32 *size);
|
||||
void logging_exit(bool buffer_busy);
|
||||
|
||||
#endif /* _MC_LOGGING_H_ */
|
||||
743
drivers/gud/MobiCoreDriver/main.c
Normal file
743
drivers/gud/MobiCoreDriver/main.c
Normal file
|
|
@ -0,0 +1,743 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include "mc_user.h"
|
||||
#include "mc_admin.h" /* MC_ADMIN_DEVNODE */
|
||||
|
||||
#include "platform.h" /* MC_PM_RUNTIME */
|
||||
#include "main.h"
|
||||
#include "arm.h"
|
||||
#include "admin.h"
|
||||
#include "user.h"
|
||||
#include "iwp.h"
|
||||
#include "mcp.h"
|
||||
#include "nq.h"
|
||||
#include "client.h"
|
||||
#include "xen_be.h"
|
||||
#include "xen_fe.h"
|
||||
#include "build_tag.h"
|
||||
|
||||
/* Default entry for our driver in device tree */
|
||||
#ifndef MC_DEVICE_PROPNAME
|
||||
#define MC_DEVICE_PROPNAME "arm,mcd"
|
||||
#endif
|
||||
|
||||
/* Define a MobiCore device structure for use with dev_debug() etc */
|
||||
static struct device_driver driver = {
|
||||
.name = "Trustonic"
|
||||
};
|
||||
|
||||
static struct device device = {
|
||||
.driver = &driver
|
||||
};
|
||||
|
||||
struct mc_device_ctx g_ctx = {
|
||||
.mcd = &device
|
||||
};
|
||||
|
||||
static struct {
|
||||
/* Device tree compatibility */
|
||||
bool use_platform_driver;
|
||||
/* TEE start return code mutex */
|
||||
struct mutex start_mutex;
|
||||
/* TEE start return code */
|
||||
int start_ret;
|
||||
#ifdef MC_PM_RUNTIME
|
||||
/* Whether hibernation succeeded */
|
||||
bool did_hibernate;
|
||||
/* Reboot notifications */
|
||||
struct notifier_block reboot_notifier;
|
||||
/* PM notifications */
|
||||
struct notifier_block pm_notifier;
|
||||
#endif
|
||||
/* Devices */
|
||||
dev_t device;
|
||||
struct class *class;
|
||||
/* Admin device */
|
||||
struct cdev admin_cdev;
|
||||
/* User device */
|
||||
dev_t user_dev;
|
||||
struct cdev user_cdev;
|
||||
/* Debug counters */
|
||||
struct mutex struct_counters_buf_mutex;
|
||||
char struct_counters_buf[256];
|
||||
int struct_counters_buf_len;
|
||||
} main_ctx;
|
||||
|
||||
static int mobicore_start(void);
|
||||
static void mobicore_stop(void);
|
||||
|
||||
int kasnprintf(struct kasnprintf_buf *buf, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int max_size = buf->size - buf->off;
|
||||
int i;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsnprintf(buf->buf + buf->off, max_size, fmt, args);
|
||||
if (i >= max_size) {
|
||||
int new_size = PAGE_ALIGN(buf->size + i + 1);
|
||||
char *new_buf = krealloc(buf->buf, new_size, buf->gfp);
|
||||
|
||||
if (!new_buf) {
|
||||
i = -ENOMEM;
|
||||
} else {
|
||||
buf->buf = new_buf;
|
||||
buf->size = new_size;
|
||||
max_size = buf->size - buf->off;
|
||||
i = vsnprintf(buf->buf + buf->off, max_size, fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
buf->off += i;
|
||||
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline void kasnprintf_buf_reset(struct kasnprintf_buf *buf)
|
||||
{
|
||||
kfree(buf->buf);
|
||||
buf->buf = NULL;
|
||||
buf->size = 0;
|
||||
buf->off = 0;
|
||||
}
|
||||
|
||||
ssize_t debug_generic_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos,
|
||||
int (*function)(struct kasnprintf_buf *buf))
|
||||
{
|
||||
struct kasnprintf_buf *buf = file->private_data;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&buf->mutex);
|
||||
/* Add/update buffer */
|
||||
if (!*ppos) {
|
||||
kasnprintf_buf_reset(buf);
|
||||
ret = function(buf);
|
||||
if (ret < 0) {
|
||||
kasnprintf_buf_reset(buf);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
ret = simple_read_from_buffer(user_buf, count, ppos, buf->buf,
|
||||
buf->off);
|
||||
|
||||
end:
|
||||
mutex_unlock(&buf->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int debug_generic_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct kasnprintf_buf *buf;
|
||||
|
||||
file->private_data = kzalloc(sizeof(*buf), GFP_KERNEL);
|
||||
if (!file->private_data)
|
||||
return -ENOMEM;
|
||||
|
||||
buf = file->private_data;
|
||||
mutex_init(&buf->mutex);
|
||||
buf->gfp = GFP_KERNEL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int debug_generic_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct kasnprintf_buf *buf = file->private_data;
|
||||
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
kasnprintf_buf_reset(buf);
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t debug_structs_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return debug_generic_read(file, user_buf, count, ppos,
|
||||
clients_debug_structs);
|
||||
}
|
||||
|
||||
static const struct file_operations debug_structs_ops = {
|
||||
.read = debug_structs_read,
|
||||
.llseek = default_llseek,
|
||||
.open = debug_generic_open,
|
||||
.release = debug_generic_release,
|
||||
};
|
||||
|
||||
static ssize_t debug_struct_counters_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
if (!*ppos) {
|
||||
int ret;
|
||||
|
||||
mutex_lock(&main_ctx.struct_counters_buf_mutex);
|
||||
ret = snprintf(main_ctx.struct_counters_buf,
|
||||
sizeof(main_ctx.struct_counters_buf),
|
||||
"clients: %d\n"
|
||||
"cbufs: %d\n"
|
||||
"cwsms: %d\n"
|
||||
"sessions: %d\n"
|
||||
"swsms: %d\n"
|
||||
"mmus: %d\n"
|
||||
"maps: %d\n"
|
||||
"slots: %d\n"
|
||||
"xen maps: %d\n"
|
||||
"xen fes: %d\n",
|
||||
atomic_read(&g_ctx.c_clients),
|
||||
atomic_read(&g_ctx.c_cbufs),
|
||||
atomic_read(&g_ctx.c_cwsms),
|
||||
atomic_read(&g_ctx.c_sessions),
|
||||
atomic_read(&g_ctx.c_wsms),
|
||||
atomic_read(&g_ctx.c_mmus),
|
||||
atomic_read(&g_ctx.c_maps),
|
||||
atomic_read(&g_ctx.c_slots),
|
||||
atomic_read(&g_ctx.c_xen_maps),
|
||||
atomic_read(&g_ctx.c_xen_fes));
|
||||
mutex_unlock(&main_ctx.struct_counters_buf_mutex);
|
||||
if (ret > 0)
|
||||
main_ctx.struct_counters_buf_len = ret;
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
main_ctx.struct_counters_buf,
|
||||
main_ctx.struct_counters_buf_len);
|
||||
}
|
||||
|
||||
static const struct file_operations debug_struct_counters_ops = {
|
||||
.read = debug_struct_counters_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static inline int device_user_init(void)
|
||||
{
|
||||
struct device *dev;
|
||||
int ret = 0;
|
||||
|
||||
main_ctx.user_dev = MKDEV(MAJOR(main_ctx.device), 1);
|
||||
/* Create the user node */
|
||||
mc_user_init(&main_ctx.user_cdev);
|
||||
ret = cdev_add(&main_ctx.user_cdev, main_ctx.user_dev, 1);
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "user cdev_add failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
main_ctx.user_cdev.owner = THIS_MODULE;
|
||||
dev = device_create(main_ctx.class, NULL, main_ctx.user_dev, NULL,
|
||||
MC_USER_DEVNODE);
|
||||
if (IS_ERR(dev)) {
|
||||
ret = PTR_ERR(dev);
|
||||
cdev_del(&main_ctx.user_cdev);
|
||||
mc_dev_err(ret, "user device_create failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create debugfs structs entry */
|
||||
debugfs_create_file("structs", 0400, g_ctx.debug_dir, NULL,
|
||||
&debug_structs_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void device_user_exit(void)
|
||||
{
|
||||
device_destroy(main_ctx.class, main_ctx.user_dev);
|
||||
cdev_del(&main_ctx.user_cdev);
|
||||
}
|
||||
|
||||
#ifdef MC_PM_RUNTIME
|
||||
static int reboot_notifier(struct notifier_block *nb, unsigned long event,
|
||||
void *dummy)
|
||||
{
|
||||
switch (event) {
|
||||
case SYS_HALT:
|
||||
case SYS_POWER_OFF:
|
||||
main_ctx.did_hibernate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int suspend_notifier(struct notifier_block *nb, unsigned long event,
|
||||
void *dummy)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
main_ctx.did_hibernate = false;
|
||||
switch (event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
return nq_suspend();
|
||||
case PM_POST_SUSPEND:
|
||||
return nq_resume();
|
||||
#ifdef TRUSTONIC_HIBERNATION_SUPPORT
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
/* Try to stop the TEE nicely (ignore failure) */
|
||||
nq_stop();
|
||||
break;
|
||||
case PM_POST_HIBERNATION:
|
||||
if (main_ctx.did_hibernate) {
|
||||
/* Really did hibernate */
|
||||
client_cleanup();
|
||||
main_ctx.start_ret = TEE_START_NOT_TRIGGERED;
|
||||
return mobicore_start();
|
||||
}
|
||||
|
||||
/* Did not hibernate, just restart the TEE */
|
||||
ret = nq_start();
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* MC_PM_RUNTIME */
|
||||
|
||||
static inline int check_version(void)
|
||||
{
|
||||
struct mc_version_info version_info;
|
||||
int ret;
|
||||
|
||||
/* Must be called before creating the user device node to avoid race */
|
||||
ret = mcp_get_version(&version_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* CMP version is meaningless in this case and is thus not printed */
|
||||
mc_dev_info("\n"
|
||||
" product_id = %s\n"
|
||||
" version_mci = 0x%08x\n"
|
||||
" version_so = 0x%08x\n"
|
||||
" version_mclf = 0x%08x\n"
|
||||
" version_container = 0x%08x\n"
|
||||
" version_mc_config = 0x%08x\n"
|
||||
" version_tl_api = 0x%08x\n"
|
||||
" version_dr_api = 0x%08x\n"
|
||||
" version_nwd = 0x%08x\n",
|
||||
version_info.product_id,
|
||||
version_info.version_mci,
|
||||
version_info.version_so,
|
||||
version_info.version_mclf,
|
||||
version_info.version_container,
|
||||
version_info.version_mc_config,
|
||||
version_info.version_tl_api,
|
||||
version_info.version_dr_api,
|
||||
version_info.version_nwd);
|
||||
|
||||
/* Determine which features are supported */
|
||||
if (version_info.version_mci != MC_VERSION(1, 7)) {
|
||||
ret = -EHOSTDOWN;
|
||||
mc_dev_err(ret, "TEE incompatible with this driver");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mobicore_start_domu(void)
|
||||
{
|
||||
mutex_lock(&main_ctx.start_mutex);
|
||||
if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED)
|
||||
goto end;
|
||||
|
||||
/* Must be called before creating the user device node to avoid race */
|
||||
main_ctx.start_ret = check_version();
|
||||
if (main_ctx.start_ret)
|
||||
goto end;
|
||||
|
||||
main_ctx.start_ret = device_user_init();
|
||||
end:
|
||||
mutex_unlock(&main_ctx.start_mutex);
|
||||
return main_ctx.start_ret;
|
||||
}
|
||||
|
||||
static int mobicore_start(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&main_ctx.start_mutex);
|
||||
if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED)
|
||||
goto got_ret;
|
||||
|
||||
ret = nq_start();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "NQ start failed");
|
||||
goto err_nq;
|
||||
}
|
||||
|
||||
ret = mcp_start();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "MCP start failed");
|
||||
goto err_mcp;
|
||||
}
|
||||
|
||||
ret = iwp_start();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "IWP start failed");
|
||||
goto err_iwp;
|
||||
}
|
||||
|
||||
/* Must be called before creating the user device node to avoid race */
|
||||
ret = check_version();
|
||||
if (ret)
|
||||
goto err_version;
|
||||
|
||||
#ifdef MC_PM_RUNTIME
|
||||
main_ctx.reboot_notifier.notifier_call = reboot_notifier;
|
||||
ret = register_reboot_notifier(&main_ctx.reboot_notifier);
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "reboot notifier registration failed");
|
||||
goto err_pm_notif;
|
||||
}
|
||||
|
||||
main_ctx.pm_notifier.notifier_call = suspend_notifier;
|
||||
ret = register_pm_notifier(&main_ctx.pm_notifier);
|
||||
if (ret) {
|
||||
unregister_reboot_notifier(&main_ctx.reboot_notifier);
|
||||
mc_dev_err(ret, "PM notifier register failed");
|
||||
goto err_pm_notif;
|
||||
}
|
||||
#if KERNEL_VERSION(4, 6, 0) <= LINUX_VERSION_CODE
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Trustonic",
|
||||
nq_cpu_on, nq_cpu_off);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (is_xen_dom0()) {
|
||||
ret = xen_be_init();
|
||||
if (ret)
|
||||
goto err_xen_be;
|
||||
}
|
||||
|
||||
ret = device_user_init();
|
||||
if (ret)
|
||||
goto err_device_user;
|
||||
|
||||
main_ctx.start_ret = 0;
|
||||
goto got_ret;
|
||||
|
||||
err_device_user:
|
||||
if (is_xen_dom0())
|
||||
xen_be_exit();
|
||||
err_xen_be:
|
||||
#ifdef MC_PM_RUNTIME
|
||||
unregister_reboot_notifier(&main_ctx.reboot_notifier);
|
||||
unregister_pm_notifier(&main_ctx.pm_notifier);
|
||||
err_pm_notif:
|
||||
#endif
|
||||
err_version:
|
||||
iwp_stop();
|
||||
err_iwp:
|
||||
mcp_stop();
|
||||
err_mcp:
|
||||
nq_stop();
|
||||
err_nq:
|
||||
main_ctx.start_ret = ret;
|
||||
got_ret:
|
||||
mutex_unlock(&main_ctx.start_mutex);
|
||||
return main_ctx.start_ret;
|
||||
}
|
||||
|
||||
static void mobicore_stop(void)
|
||||
{
|
||||
device_user_exit();
|
||||
if (is_xen_dom0())
|
||||
xen_be_exit();
|
||||
|
||||
if (!is_xen_domu()) {
|
||||
#ifdef MC_PM_RUNTIME
|
||||
unregister_reboot_notifier(&main_ctx.reboot_notifier);
|
||||
unregister_pm_notifier(&main_ctx.pm_notifier);
|
||||
#endif
|
||||
iwp_stop();
|
||||
mcp_stop();
|
||||
nq_stop();
|
||||
}
|
||||
}
|
||||
|
||||
int mc_wait_tee_start(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&main_ctx.start_mutex);
|
||||
while (main_ctx.start_ret == TEE_START_NOT_TRIGGERED) {
|
||||
mutex_unlock(&main_ctx.start_mutex);
|
||||
ssleep(1);
|
||||
mutex_lock(&main_ctx.start_mutex);
|
||||
}
|
||||
|
||||
ret = main_ctx.start_ret;
|
||||
mutex_unlock(&main_ctx.start_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int device_common_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = alloc_chrdev_region(&main_ctx.device, 0, 2, "trustonic_tee");
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "alloc_chrdev_region failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
main_ctx.class = class_create(THIS_MODULE, "trustonic_tee");
|
||||
if (IS_ERR(main_ctx.class)) {
|
||||
ret = PTR_ERR(main_ctx.class);
|
||||
mc_dev_err(ret, "class_create failed");
|
||||
unregister_chrdev_region(main_ctx.device, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void device_common_exit(void)
|
||||
{
|
||||
class_destroy(main_ctx.class);
|
||||
unregister_chrdev_region(main_ctx.device, 2);
|
||||
}
|
||||
|
||||
static inline int device_admin_init(void)
|
||||
{
|
||||
struct device *dev;
|
||||
int ret = 0;
|
||||
|
||||
/* Create the ADMIN node */
|
||||
ret = mc_admin_init(&main_ctx.admin_cdev, mobicore_start,
|
||||
mobicore_stop);
|
||||
if (ret)
|
||||
goto err_init;
|
||||
|
||||
ret = cdev_add(&main_ctx.admin_cdev, main_ctx.device, 1);
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "admin cdev_add failed");
|
||||
goto err_cdev;
|
||||
}
|
||||
|
||||
main_ctx.admin_cdev.owner = THIS_MODULE;
|
||||
dev = device_create(main_ctx.class, NULL, main_ctx.device, NULL,
|
||||
MC_ADMIN_DEVNODE);
|
||||
if (IS_ERR(dev)) {
|
||||
ret = PTR_ERR(dev);
|
||||
mc_dev_err(ret, "admin device_create failed");
|
||||
goto err_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_device:
|
||||
cdev_del(&main_ctx.admin_cdev);
|
||||
err_cdev:
|
||||
mc_admin_exit();
|
||||
err_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void device_admin_exit(void)
|
||||
{
|
||||
device_destroy(main_ctx.class, main_ctx.device);
|
||||
cdev_del(&main_ctx.admin_cdev);
|
||||
mc_admin_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called by the kernel during startup or by a insmod command.
|
||||
* This device is installed and registered as cdev, then interrupt and
|
||||
* queue handling is set up
|
||||
*/
|
||||
static int mobicore_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pdev)
|
||||
g_ctx.mcd->of_node = pdev->dev.of_node;
|
||||
|
||||
#ifdef MOBICORE_COMPONENT_BUILD_TAG
|
||||
mc_dev_info("MobiCore %s", MOBICORE_COMPONENT_BUILD_TAG);
|
||||
#endif
|
||||
/* Hardware does not support ARM TrustZone -> Cannot continue! */
|
||||
if (!is_xen_domu() && !has_security_extensions()) {
|
||||
ret = -ENODEV;
|
||||
mc_dev_err(ret, "Hardware doesn't support ARM TrustZone!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Running in secure mode -> Cannot load the driver! */
|
||||
if (is_secure_mode()) {
|
||||
ret = -ENODEV;
|
||||
mc_dev_err(ret, "Running in secure MODE!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Make sure we can create debugfs entries */
|
||||
g_ctx.debug_dir = debugfs_create_dir("trustonic_tee", NULL);
|
||||
|
||||
/* Initialize debug counters */
|
||||
atomic_set(&g_ctx.c_clients, 0);
|
||||
atomic_set(&g_ctx.c_cbufs, 0);
|
||||
atomic_set(&g_ctx.c_cwsms, 0);
|
||||
atomic_set(&g_ctx.c_sessions, 0);
|
||||
atomic_set(&g_ctx.c_wsms, 0);
|
||||
atomic_set(&g_ctx.c_mmus, 0);
|
||||
atomic_set(&g_ctx.c_maps, 0);
|
||||
atomic_set(&g_ctx.c_slots, 0);
|
||||
atomic_set(&g_ctx.c_xen_maps, 0);
|
||||
atomic_set(&g_ctx.c_xen_fes, 0);
|
||||
main_ctx.start_ret = TEE_START_NOT_TRIGGERED;
|
||||
mutex_init(&main_ctx.start_mutex);
|
||||
mutex_init(&main_ctx.struct_counters_buf_mutex);
|
||||
/* Create debugfs info entries */
|
||||
debugfs_create_file("structs_counters", 0400, g_ctx.debug_dir, NULL,
|
||||
&debug_struct_counters_ops);
|
||||
|
||||
/* Initialize common API layer */
|
||||
client_init();
|
||||
|
||||
/* Initialize plenty of nice features */
|
||||
ret = nq_init();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "NQ init failed");
|
||||
goto fail_nq_init;
|
||||
}
|
||||
|
||||
ret = mcp_init();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "MCP init failed");
|
||||
goto err_mcp;
|
||||
}
|
||||
|
||||
ret = iwp_init();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "IWP init failed");
|
||||
goto err_iwp;
|
||||
}
|
||||
|
||||
ret = device_common_init();
|
||||
if (ret)
|
||||
goto err_common;
|
||||
|
||||
if (!is_xen_domu()) {
|
||||
/* Admin dev is for the daemon to communicate with the driver */
|
||||
ret = device_admin_init();
|
||||
if (ret)
|
||||
goto err_admin;
|
||||
|
||||
#ifndef MC_DELAYED_TEE_START
|
||||
ret = mobicore_start();
|
||||
#endif
|
||||
if (ret)
|
||||
goto err_start;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_start:
|
||||
device_admin_exit();
|
||||
err_admin:
|
||||
device_common_exit();
|
||||
err_common:
|
||||
iwp_exit();
|
||||
err_iwp:
|
||||
mcp_exit();
|
||||
err_mcp:
|
||||
nq_exit();
|
||||
fail_nq_init:
|
||||
debugfs_remove_recursive(g_ctx.debug_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mobicore_probe_not_of(void)
|
||||
{
|
||||
return mobicore_probe(NULL);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match_table[] = {
|
||||
{ .compatible = MC_DEVICE_PROPNAME },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver mc_plat_driver = {
|
||||
.probe = mobicore_probe,
|
||||
.driver = {
|
||||
.name = "mcd",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_table,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init mobicore_init(void)
|
||||
{
|
||||
dev_set_name(g_ctx.mcd, "TEE");
|
||||
/*
|
||||
* Do not remove or change the following trace.
|
||||
* The string "MobiCore" is used to detect if the TEE is in of the image
|
||||
*/
|
||||
mc_dev_info("MobiCore mcDrvModuleApi version is %d.%d",
|
||||
MCDRVMODULEAPI_VERSION_MAJOR,
|
||||
MCDRVMODULEAPI_VERSION_MINOR);
|
||||
|
||||
/* In a Xen DomU, just register the front-end */
|
||||
if (is_xen_domu())
|
||||
return xen_fe_init(mobicore_probe_not_of, mobicore_start_domu);
|
||||
|
||||
main_ctx.use_platform_driver =
|
||||
of_find_compatible_node(NULL, NULL, MC_DEVICE_PROPNAME);
|
||||
if (main_ctx.use_platform_driver)
|
||||
return platform_driver_register(&mc_plat_driver);
|
||||
|
||||
return mobicore_probe_not_of();
|
||||
}
|
||||
|
||||
static void __exit mobicore_exit(void)
|
||||
{
|
||||
if (is_xen_domu())
|
||||
xen_fe_exit();
|
||||
|
||||
if (main_ctx.use_platform_driver)
|
||||
platform_driver_unregister(&mc_plat_driver);
|
||||
|
||||
if (!is_xen_domu())
|
||||
device_admin_exit();
|
||||
|
||||
device_common_exit();
|
||||
iwp_exit();
|
||||
mcp_exit();
|
||||
nq_exit();
|
||||
debugfs_remove_recursive(g_ctx.debug_dir);
|
||||
}
|
||||
|
||||
module_init(mobicore_init);
|
||||
module_exit(mobicore_exit);
|
||||
|
||||
MODULE_AUTHOR("Trustonic Limited");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MobiCore driver");
|
||||
123
drivers/gud/MobiCoreDriver/main.h
Normal file
123
drivers/gud/MobiCoreDriver/main.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_MAIN_H_
|
||||
#define _MC_MAIN_H_
|
||||
|
||||
#include <linux/device.h> /* dev_* macros */
|
||||
#include <linux/slab.h> /* gfp_t */
|
||||
#include <linux/fs.h> /* struct inode and struct file */
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/version.h>
|
||||
#include <xen/xen.h>
|
||||
|
||||
#define MC_VERSION(major, minor) \
|
||||
((((major) & 0x0000ffff) << 16) | ((minor) & 0x0000ffff))
|
||||
#define MC_VERSION_MAJOR(x) ((x) >> 16)
|
||||
#define MC_VERSION_MINOR(x) ((x) & 0xffff)
|
||||
|
||||
#define mc_dev_err(__ret__, fmt, ...) \
|
||||
dev_err(g_ctx.mcd, "ERROR %d %s: " fmt "\n", \
|
||||
__ret__, __func__, ##__VA_ARGS__)
|
||||
|
||||
#define mc_dev_info(fmt, ...) \
|
||||
dev_info(g_ctx.mcd, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define mc_dev_devel(fmt, ...) \
|
||||
dev_info(g_ctx.mcd, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
|
||||
#else /* DEBUG */
|
||||
#define mc_dev_devel(...) do {} while (0)
|
||||
#endif /* !DEBUG */
|
||||
|
||||
#define TEEC_TT_LOGIN_KERNEL 0x80000000
|
||||
|
||||
#define TEE_START_NOT_TRIGGERED 1
|
||||
|
||||
/* MobiCore Driver Kernel Module context data. */
|
||||
struct mc_device_ctx {
|
||||
struct device *mcd;
|
||||
/* debugfs root */
|
||||
struct dentry *debug_dir;
|
||||
|
||||
/* Debug counters */
|
||||
atomic_t c_clients;
|
||||
atomic_t c_cbufs;
|
||||
atomic_t c_cwsms;
|
||||
atomic_t c_sessions;
|
||||
atomic_t c_wsms;
|
||||
atomic_t c_mmus;
|
||||
atomic_t c_maps;
|
||||
atomic_t c_slots;
|
||||
atomic_t c_xen_maps;
|
||||
atomic_t c_xen_fes;
|
||||
};
|
||||
|
||||
extern struct mc_device_ctx g_ctx;
|
||||
|
||||
/* Debug stuff */
|
||||
struct kasnprintf_buf {
|
||||
struct mutex mutex; /* Protect buf/size/off access */
|
||||
gfp_t gfp;
|
||||
void *buf;
|
||||
int size;
|
||||
int off;
|
||||
};
|
||||
|
||||
/* Wait for TEE to start and get status */
|
||||
int mc_wait_tee_start(void);
|
||||
|
||||
extern __printf(2, 3)
|
||||
int kasnprintf(struct kasnprintf_buf *buf, const char *fmt, ...);
|
||||
ssize_t debug_generic_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos,
|
||||
int (*function)(struct kasnprintf_buf *buf));
|
||||
int debug_generic_open(struct inode *inode, struct file *file);
|
||||
int debug_generic_release(struct inode *inode, struct file *file);
|
||||
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
static inline unsigned int kref_read(struct kref *kref)
|
||||
{
|
||||
return atomic_read(&kref->refcount);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Xen support */
|
||||
|
||||
#ifdef CONFIG_XEN
|
||||
#if KERNEL_VERSION(4, 4, 0) <= LINUX_VERSION_CODE
|
||||
#define TRUSTONIC_XEN_DOMU
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static inline bool is_xen_dom0(void)
|
||||
{
|
||||
#if KERNEL_VERSION(3, 18, 0) <= LINUX_VERSION_CODE
|
||||
return xen_domain() && xen_initial_domain();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool is_xen_domu(void)
|
||||
{
|
||||
#ifdef TRUSTONIC_XEN_DOMU
|
||||
return xen_domain() && !xen_initial_domain();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _MC_MAIN_H_ */
|
||||
86
drivers/gud/MobiCoreDriver/mc_admin.h
Normal file
86
drivers/gud/MobiCoreDriver/mc_admin.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MC_ADMIN_IOCTL_H__
|
||||
#define __MC_ADMIN_IOCTL_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MC_ADMIN_DEVNODE "mobicore"
|
||||
|
||||
/* Driver/daemon commands */
|
||||
enum {
|
||||
/* Command 0 is reserved */
|
||||
MC_DRV_GET_ROOT_CONTAINER = 1,
|
||||
MC_DRV_GET_SP_CONTAINER = 2,
|
||||
MC_DRV_GET_TRUSTLET_CONTAINER = 3,
|
||||
MC_DRV_GET_TRUSTLET = 4,
|
||||
MC_DRV_SIGNAL_CRASH = 5,
|
||||
};
|
||||
|
||||
/* MobiCore IOCTL magic number */
|
||||
#define MC_IOC_MAGIC 'M'
|
||||
|
||||
struct mc_admin_request {
|
||||
__u32 request_id; /* Unique request identifier */
|
||||
__u32 command; /* Command to daemon */
|
||||
struct mc_uuid_t uuid; /* UUID of trustlet, if relevant */
|
||||
__u32 is_gp; /* Whether trustlet is GP */
|
||||
__u32 spid; /* SPID of trustlet, if relevant */
|
||||
};
|
||||
|
||||
struct mc_admin_response {
|
||||
__u32 request_id; /* Unique request identifier */
|
||||
__u32 error_no; /* Errno from daemon */
|
||||
__u32 spid; /* SPID of trustlet, if relevant */
|
||||
__u32 service_type; /* Type of trustlet being returned */
|
||||
__u32 length; /* Length of data to get */
|
||||
/* Any data follows */
|
||||
};
|
||||
|
||||
struct mc_admin_driver_info {
|
||||
/* Version, and something else..*/
|
||||
__u32 drv_version;
|
||||
__u32 initial_cmd_id;
|
||||
};
|
||||
|
||||
struct mc_admin_load_info {
|
||||
__u32 spid; /* SPID of trustlet, if relevant */
|
||||
__u64 address; /* Address of the data */
|
||||
__u32 length; /* Length of data to get */
|
||||
struct mc_uuid_t uuid; /* UUID of trustlet, if relevant */
|
||||
};
|
||||
|
||||
#define MC_ADMIN_IO_GET_DRIVER_REQUEST \
|
||||
_IOR(MC_IOC_MAGIC, 0, struct mc_admin_request)
|
||||
#define MC_ADMIN_IO_GET_INFO \
|
||||
_IOR(MC_IOC_MAGIC, 1, struct mc_admin_driver_info)
|
||||
#define MC_ADMIN_IO_LOAD_DRIVER \
|
||||
_IOW(MC_IOC_MAGIC, 2, struct mc_admin_load_info)
|
||||
#define MC_ADMIN_IO_LOAD_TOKEN \
|
||||
_IOW(MC_IOC_MAGIC, 3, struct mc_admin_load_info)
|
||||
#define MC_ADMIN_IO_LOAD_CHECK \
|
||||
_IOW(MC_IOC_MAGIC, 4, struct mc_admin_load_info)
|
||||
#define MC_ADMIN_IO_LOAD_KEY_SO \
|
||||
_IOW(MC_IOC_MAGIC, 5, struct mc_admin_load_info)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* __MC_ADMIN_IOCTL_H__ */
|
||||
30
drivers/gud/MobiCoreDriver/mc_linux_api.h
Normal file
30
drivers/gud/MobiCoreDriver/mc_linux_api.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2019 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _MC_LINUX_API_H_
|
||||
#define _MC_LINUX_API_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Manage dynamically switch TEE worker threads/TEE affinity to big core only.
|
||||
* Or default core affinity.
|
||||
* Warning: Both PLAT_DEFAULT_TEE_AFFINITY_MASK and
|
||||
* BIG_CORE_SWITCH_AFFINITY_MASK have to be defined
|
||||
*/
|
||||
#if defined(BIG_CORE_SWITCH_AFFINITY_MASK)
|
||||
void set_tee_worker_threads_on_big_core(bool big_core);
|
||||
#endif
|
||||
|
||||
#endif /* _MC_LINUX_API_H_ */
|
||||
307
drivers/gud/MobiCoreDriver/mc_user.h
Normal file
307
drivers/gud/MobiCoreDriver/mc_user.h
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_USER_H_
|
||||
#define _MC_USER_H_
|
||||
|
||||
#define MCDRVMODULEAPI_VERSION_MAJOR 7
|
||||
#define MCDRVMODULEAPI_VERSION_MINOR 0
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#define BIT(n) (1 << (n))
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#define MC_USER_DEVNODE "mobicore-user"
|
||||
|
||||
/** Maximum length of MobiCore product ID string. */
|
||||
#define MC_PRODUCT_ID_LEN 64
|
||||
|
||||
/** Number of buffers that can be mapped at once */
|
||||
#define MC_MAP_MAX 4
|
||||
|
||||
/* Max length for buffers */
|
||||
#define MC_MAX_TCI_LEN 0x100000
|
||||
#define BUFFER_LENGTH_MAX 0x40000000
|
||||
|
||||
/* Max length for objects */
|
||||
#define OBJECT_LENGTH_MAX 0x8000000
|
||||
|
||||
/* Flags for buffers to map (aligned on GP) */
|
||||
#define MC_IO_MAP_INPUT BIT(0)
|
||||
#define MC_IO_MAP_OUTPUT BIT(1)
|
||||
#define MC_IO_MAP_INPUT_OUTPUT (MC_IO_MAP_INPUT | MC_IO_MAP_OUTPUT)
|
||||
|
||||
/*
|
||||
* Universally Unique Identifier (UUID) according to ISO/IEC 11578.
|
||||
*/
|
||||
struct mc_uuid_t {
|
||||
__u8 value[16]; /* Value of the UUID */
|
||||
};
|
||||
|
||||
/*
|
||||
* GP TA login types.
|
||||
*/
|
||||
enum mc_login_type {
|
||||
LOGIN_PUBLIC = 0,
|
||||
LOGIN_USER,
|
||||
LOGIN_GROUP,
|
||||
LOGIN_APPLICATION = 4,
|
||||
LOGIN_USER_APPLICATION,
|
||||
LOGIN_GROUP_APPLICATION,
|
||||
};
|
||||
|
||||
/*
|
||||
* GP TA identity structure.
|
||||
*/
|
||||
struct mc_identity {
|
||||
enum mc_login_type login_type;
|
||||
union {
|
||||
__u8 login_data[16];
|
||||
gid_t gid; /* Requested group id */
|
||||
struct {
|
||||
uid_t euid;
|
||||
uid_t ruid;
|
||||
} uid;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_OPEN_SESSION ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_open_session {
|
||||
struct mc_uuid_t uuid; /* trustlet uuid */
|
||||
__u32 is_gp_uuid; /* uuid is for GP TA */
|
||||
__u32 sid; /* session id (out) */
|
||||
__u64 tci; /* tci buffer pointer */
|
||||
__u32 tcilen; /* tci length */
|
||||
struct mc_identity identity; /* GP TA identity */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_OPEN_TRUSTLET ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_open_trustlet {
|
||||
__u32 sid; /* session id (out) */
|
||||
__u32 spid; /* trustlet spid */
|
||||
__u64 buffer; /* trustlet binary pointer */
|
||||
__u32 tlen; /* binary length */
|
||||
__u64 tci; /* tci buffer pointer */
|
||||
__u32 tcilen; /* tci length */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_WAIT ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_wait {
|
||||
__u32 sid; /* session id (in) */
|
||||
__s32 timeout; /* notification timeout */
|
||||
__u32 partial; /* for proxy server to retry silently */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_ALLOC ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_alloc {
|
||||
__u32 len; /* buffer length */
|
||||
__u32 handle; /* user handle for the buffer (out) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Buffer mapping incoming and outgoing information.
|
||||
*/
|
||||
struct mc_ioctl_buffer {
|
||||
__u64 va; /* user space address of buffer */
|
||||
__u32 len; /* buffer length */
|
||||
__u64 sva; /* SWd virt address of buffer (out) */
|
||||
__u32 flags; /* buffer flags */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_MAP and MC_IO_UNMAP ioctl commands.
|
||||
*/
|
||||
struct mc_ioctl_map {
|
||||
__u32 sid; /* session id */
|
||||
struct mc_ioctl_buffer buf; /* buffers info */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_ERR ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_geterr {
|
||||
__u32 sid; /* session id */
|
||||
__s32 value; /* error value (out) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Global MobiCore Version Information.
|
||||
*/
|
||||
struct mc_version_info {
|
||||
char product_id[MC_PRODUCT_ID_LEN]; /* Product ID string */
|
||||
__u32 version_mci; /* Mobicore Control Interface */
|
||||
__u32 version_so; /* Secure Objects */
|
||||
__u32 version_mclf; /* MobiCore Load Format */
|
||||
__u32 version_container; /* MobiCore Container Format */
|
||||
__u32 version_mc_config; /* MobiCore Config. Block Format */
|
||||
__u32 version_tl_api; /* MobiCore Trustlet API */
|
||||
__u32 version_dr_api; /* MobiCore Driver API */
|
||||
__u32 version_nwd; /* This Driver */
|
||||
};
|
||||
|
||||
/*
|
||||
* GP TA operation structure.
|
||||
*/
|
||||
struct gp_value {
|
||||
__u32 a;
|
||||
__u32 b;
|
||||
};
|
||||
|
||||
struct gp_temp_memref {
|
||||
__u64 buffer;
|
||||
__u64 size;
|
||||
};
|
||||
|
||||
struct gp_shared_memory {
|
||||
__u64 buffer;
|
||||
__u64 size;
|
||||
__u32 flags;
|
||||
};
|
||||
|
||||
struct gp_regd_memref {
|
||||
struct gp_shared_memory parent;
|
||||
__u64 size;
|
||||
__u64 offset;
|
||||
};
|
||||
|
||||
union gp_param {
|
||||
struct gp_temp_memref tmpref;
|
||||
struct gp_regd_memref memref;
|
||||
struct gp_value value;
|
||||
};
|
||||
|
||||
struct gp_operation {
|
||||
__u32 started;
|
||||
__u32 param_types;
|
||||
union gp_param params[4];
|
||||
};
|
||||
|
||||
struct gp_return {
|
||||
__u32 origin;
|
||||
__u32 value;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_INITIALIZE_CONTEXT ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_initialize_context {
|
||||
struct gp_return ret; /* return origin/value (out) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_REGISTER_SHARED_MEM ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_register_shared_mem {
|
||||
struct gp_shared_memory memref;
|
||||
struct gp_return ret; /* return origin/value (out) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_RELEASE_SHARED_MEM ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_release_shared_mem {
|
||||
struct gp_shared_memory memref;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_OPEN_SESSION ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_open_session {
|
||||
struct mc_uuid_t uuid; /* trustlet uuid */
|
||||
struct mc_identity identity; /* GP TA identity */
|
||||
struct gp_operation operation; /* set of parameters */
|
||||
struct gp_return ret; /* return origin/value (out) */
|
||||
__u32 session_id; /* session id (out) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_CLOSE_SESSION ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_close_session {
|
||||
__u32 session_id; /* session id */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_INVOKE_COMMAND ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_invoke_command {
|
||||
struct gp_operation operation; /* set of parameters */
|
||||
__u32 session_id; /* session id */
|
||||
__u32 command_id; /* ID of the command */
|
||||
struct gp_return ret; /* return origin/value (out) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data exchange structure of the MC_IO_GP_CANCEL ioctl command.
|
||||
*/
|
||||
struct mc_ioctl_gp_request_cancellation {
|
||||
struct gp_operation operation; /* set of parameters */
|
||||
};
|
||||
|
||||
/*
|
||||
* defines for the ioctl mobicore driver module function call from user space.
|
||||
*/
|
||||
/* MobiCore IOCTL magic number */
|
||||
#define MC_IOC_MAGIC 'M'
|
||||
|
||||
/*
|
||||
* Implement corresponding functions from user api
|
||||
*/
|
||||
#define MC_IO_OPEN_SESSION \
|
||||
_IOWR(MC_IOC_MAGIC, 0, struct mc_ioctl_open_session)
|
||||
#define MC_IO_OPEN_TRUSTLET \
|
||||
_IOWR(MC_IOC_MAGIC, 1, struct mc_ioctl_open_trustlet)
|
||||
#define MC_IO_CLOSE_SESSION \
|
||||
_IO(MC_IOC_MAGIC, 2)
|
||||
#define MC_IO_NOTIFY \
|
||||
_IO(MC_IOC_MAGIC, 3)
|
||||
#define MC_IO_WAIT \
|
||||
_IOW(MC_IOC_MAGIC, 4, struct mc_ioctl_wait)
|
||||
#define MC_IO_MAP \
|
||||
_IOWR(MC_IOC_MAGIC, 5, struct mc_ioctl_map)
|
||||
#define MC_IO_UNMAP \
|
||||
_IOW(MC_IOC_MAGIC, 6, struct mc_ioctl_map)
|
||||
#define MC_IO_ERR \
|
||||
_IOWR(MC_IOC_MAGIC, 7, struct mc_ioctl_geterr)
|
||||
#define MC_IO_HAS_SESSIONS \
|
||||
_IO(MC_IOC_MAGIC, 8)
|
||||
#define MC_IO_VERSION \
|
||||
_IOR(MC_IOC_MAGIC, 9, struct mc_version_info)
|
||||
#define MC_IO_GP_INITIALIZE_CONTEXT \
|
||||
_IOW(MC_IOC_MAGIC, 20, struct mc_ioctl_gp_initialize_context)
|
||||
#define MC_IO_GP_REGISTER_SHARED_MEM \
|
||||
_IOWR(MC_IOC_MAGIC, 21, struct mc_ioctl_gp_register_shared_mem)
|
||||
#define MC_IO_GP_RELEASE_SHARED_MEM \
|
||||
_IOW(MC_IOC_MAGIC, 23, struct mc_ioctl_gp_release_shared_mem)
|
||||
#define MC_IO_GP_OPEN_SESSION \
|
||||
_IOWR(MC_IOC_MAGIC, 24, struct mc_ioctl_gp_open_session)
|
||||
#define MC_IO_GP_CLOSE_SESSION \
|
||||
_IOW(MC_IOC_MAGIC, 25, struct mc_ioctl_gp_close_session)
|
||||
#define MC_IO_GP_INVOKE_COMMAND \
|
||||
_IOWR(MC_IOC_MAGIC, 26, struct mc_ioctl_gp_invoke_command)
|
||||
#define MC_IO_GP_REQUEST_CANCELLATION \
|
||||
_IOW(MC_IOC_MAGIC, 27, struct mc_ioctl_gp_request_cancellation)
|
||||
|
||||
#endif /* _MC_USER_H_ */
|
||||
160
drivers/gud/MobiCoreDriver/mcifc.h
Normal file
160
drivers/gud/MobiCoreDriver/mcifc.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MCIFC_H_
|
||||
#define MCIFC_H_
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
/** @name MobiCore FastCall Definition
|
||||
* Defines for the two different FastCall's.
|
||||
*/
|
||||
/**/
|
||||
|
||||
/* --- global ---- */
|
||||
#define MC_FC_INVALID ((u32)0) /**< Invalid FastCall ID */
|
||||
|
||||
#if (defined(CONFIG_ARM64) && !defined(MC_ARMV7_FC)) || (defined(MC_AARCH32_FC))
|
||||
|
||||
#define FASTCALL_OWNER_TZOS (0x3F000000)
|
||||
#define FASTCALL_ATOMIC_MASK BIT(31)
|
||||
/**Trusted OS Fastcalls SMC32 */
|
||||
#define MC_FC_STD32_BASE \
|
||||
((u32)(FASTCALL_OWNER_TZOS | FASTCALL_ATOMIC_MASK))
|
||||
/* SMC32 Trusted OS owned Fastcalls */
|
||||
#define MC_FC_STD32(x) ((u32)(MC_FC_STD32_BASE + (x)))
|
||||
|
||||
#define MC_FC_INIT MC_FC_STD32(1) /**< Initializing FastCall. */
|
||||
#define MC_FC_INFO MC_FC_STD32(2) /**< Info FastCall. */
|
||||
#define MC_FC_MEM_TRACE MC_FC_STD32(10) /**< Enable SWd tracing via memory */
|
||||
|
||||
#else
|
||||
|
||||
#define MC_FC_INIT ((u32)(-1)) /**< Initializing FastCall. */
|
||||
#define MC_FC_INFO ((u32)(-2)) /**< Info FastCall. */
|
||||
#define MC_FC_MEM_TRACE ((u32)(-31)) /**< Enable SWd tracing via memory */
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name MobiCore SMC Definition
|
||||
* Defines the different secure monitor calls (SMC) for world switching.
|
||||
*/
|
||||
/**< Yield to switch from NWd to SWd. */
|
||||
#define MC_SMC_N_YIELD 3
|
||||
/**< SIQ to switch from NWd to SWd. */
|
||||
#define MC_SMC_N_SIQ 4
|
||||
/** @} */
|
||||
|
||||
/** @name MobiCore status
|
||||
* MobiCore status information.
|
||||
*/
|
||||
/**< MobiCore is not yet initialized. FastCall FcInit() to set up MobiCore.*/
|
||||
#define MC_STATUS_NOT_INITIALIZED 0
|
||||
/**< Bad parameters have been passed in FcInit(). */
|
||||
#define MC_STATUS_BAD_INIT 1
|
||||
/**< MobiCore did initialize properly. */
|
||||
#define MC_STATUS_INITIALIZED 2
|
||||
/**< MobiCore kernel halted due to an unrecoverable exception. Further
|
||||
* information is available extended info
|
||||
*/
|
||||
#define MC_STATUS_HALT 3
|
||||
/** @} */
|
||||
|
||||
/** @name Extended Info Identifiers
|
||||
* Extended info parameters for MC_FC_INFO to obtain further information
|
||||
* depending on MobiCore state.
|
||||
*/
|
||||
/**< Version of the MobiCore Control Interface (MCI) */
|
||||
#define MC_EXT_INFO_ID_MCI_VERSION 0
|
||||
/**< MobiCore control flags */
|
||||
#define MC_EXT_INFO_ID_FLAGS 1
|
||||
/**< MobiCore halt condition code */
|
||||
#define MC_EXT_INFO_ID_HALT_CODE 2
|
||||
/**< MobiCore halt condition instruction pointer */
|
||||
#define MC_EXT_INFO_ID_HALT_IP 3
|
||||
/**< MobiCore fault counter */
|
||||
#define MC_EXT_INFO_ID_FAULT_CNT 4
|
||||
/**< MobiCore last fault cause */
|
||||
#define MC_EXT_INFO_ID_FAULT_CAUSE 5
|
||||
/**< MobiCore last fault meta */
|
||||
#define MC_EXT_INFO_ID_FAULT_META 6
|
||||
/**< MobiCore last fault threadid */
|
||||
#define MC_EXT_INFO_ID_FAULT_THREAD 7
|
||||
/**< MobiCore last fault instruction pointer */
|
||||
#define MC_EXT_INFO_ID_FAULT_IP 8
|
||||
/**< MobiCore last fault stack pointer */
|
||||
#define MC_EXT_INFO_ID_FAULT_SP 9
|
||||
/**< MobiCore last fault ARM arch information */
|
||||
#define MC_EXT_INFO_ID_FAULT_ARCH_DFSR 10
|
||||
/**< MobiCore last fault ARM arch information */
|
||||
#define MC_EXT_INFO_ID_FAULT_ARCH_ADFSR 11
|
||||
/**< MobiCore last fault ARM arch information */
|
||||
#define MC_EXT_INFO_ID_FAULT_ARCH_DFAR 12
|
||||
/**< MobiCore last fault ARM arch information */
|
||||
#define MC_EXT_INFO_ID_FAULT_ARCH_IFSR 13
|
||||
/**< MobiCore last fault ARM arch information */
|
||||
#define MC_EXT_INFO_ID_FAULT_ARCH_AIFSR 14
|
||||
/**< MobiCore last fault ARM arch information */
|
||||
#define MC_EXT_INFO_ID_FAULT_ARCH_IFAR 15
|
||||
/**< MobiCore configured by Daemon via fc_init flag */
|
||||
#define MC_EXT_INFO_ID_MC_CONFIGURED 16
|
||||
/**< MobiCore scheduling status: idle/non-idle */
|
||||
#define MC_EXT_INFO_ID_MC_SCHED_STATUS 17
|
||||
/**< MobiCore runtime status: initialized, halted */
|
||||
#define MC_EXT_INFO_ID_MC_STATUS 18
|
||||
/**< MobiCore exception handler last partner */
|
||||
#define MC_EXT_INFO_ID_MC_EXC_PARTNER 19
|
||||
/**< MobiCore exception handler last peer */
|
||||
#define MC_EXT_INFO_ID_MC_EXC_IPCPEER 20
|
||||
/**< MobiCore exception handler last IPC message */
|
||||
#define MC_EXT_INFO_ID_MC_EXC_IPCMSG 21
|
||||
/**< MobiCore exception handler last IPC data */
|
||||
#define MC_EXT_INFO_ID_MC_EXC_IPCDATA 22
|
||||
/**< MobiCore exception handler last UUID (uses 4 slots: 23 to 26) */
|
||||
#define MC_EXT_INFO_ID_MC_EXC_UUID 23
|
||||
#define MC_EXT_INFO_ID_MC_EXC_UUID1 24
|
||||
#define MC_EXT_INFO_ID_MC_EXC_UUID2 25
|
||||
#define MC_EXT_INFO_ID_MC_EXC_UUID3 26
|
||||
/**< MobiCore exception handler last crashing task offset */
|
||||
#define MC_EXT_INFO_ID_TASK_OFFSET 27
|
||||
/**< MobiCore exception handler last crashing task's mclib offset */
|
||||
#define MC_EXT_INFO_ID_MCLIB_OFFSET 28
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name FastCall return values
|
||||
* Return values of the MobiCore FastCalls.
|
||||
*/
|
||||
/**< No error. Everything worked fine. */
|
||||
#define MC_FC_RET_OK 0
|
||||
/**< FastCall was not successful. */
|
||||
#define MC_FC_RET_ERR_INVALID 1
|
||||
/**< MobiCore has already been initialized. */
|
||||
#define MC_FC_RET_ERR_ALREADY_INITIALIZED 5
|
||||
/**< Call is not allowed. */
|
||||
#define TEE_FC_RET_ERR_NOABILITY 6
|
||||
/** @} */
|
||||
|
||||
/** @name Init FastCall flags
|
||||
* Return flags of the Init FastCall.
|
||||
*/
|
||||
/**< SWd uses LPAE MMU table format. */
|
||||
#define MC_FC_INIT_FLAG_LPAE BIT(0)
|
||||
/** @} */
|
||||
|
||||
#endif /** MCIFC_H_ */
|
||||
|
||||
/** @} */
|
||||
123
drivers/gud/MobiCoreDriver/mciiwp.h
Normal file
123
drivers/gud/MobiCoreDriver/mciiwp.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2016-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MCIIWP_H_
|
||||
#define MCIIWP_H_
|
||||
|
||||
#include "tee_client_types.h" /* teec_uuid FIXME it's all mixed up! */
|
||||
|
||||
/** Session ID for notifications for the Dragon CA-to-TA communication protocol
|
||||
*
|
||||
* Session ID are distinct from any valid MCP session identifier
|
||||
* and from the existing pseudo-session identifiers :
|
||||
* - SID_MCP = 0
|
||||
* - SID_INVALID = 0xffffffff
|
||||
*
|
||||
* A session ID is a thread ID, and since thread IDs have a nonzero task ID as
|
||||
* their lowest 16 bits, we can use values of the form 0x????0000
|
||||
*/
|
||||
#define SID_OPEN_SESSION (0x00010000)
|
||||
#define SID_INVOKE_COMMAND (0x00020000)
|
||||
#define SID_CLOSE_SESSION (0x00030000)
|
||||
#define SID_CANCEL_OPERATION (0x00040000)
|
||||
#define SID_MEMORY_REFERENCE (0x00050000)
|
||||
#define SID_OPEN_TA (0x00060000)
|
||||
#define SID_REQ_TA (0x00070000)
|
||||
|
||||
/* To quickly detect IWP notifications */
|
||||
#define SID_IWP_NOTIFICATION \
|
||||
(SID_OPEN_SESSION | SID_INVOKE_COMMAND | SID_CLOSE_SESSION | \
|
||||
SID_CANCEL_OPERATION | SID_MEMORY_REFERENCE | SID_OPEN_TA | SID_REQ_TA)
|
||||
|
||||
struct interworld_parameter_value {
|
||||
u32 a;
|
||||
u32 b;
|
||||
u8 unused[8];
|
||||
};
|
||||
|
||||
/** The API parameter type TEEC_MEMREF_WHOLE is translated into these types
|
||||
* and does not appear in the inter-world protocol.
|
||||
*
|
||||
* - memref_handle references a previously registered memory reference
|
||||
* 'offset' bytes <= memref_handle < 'offset + size' bytes
|
||||
*
|
||||
* These sizes must be contained within the memory reference.
|
||||
*/
|
||||
struct interworld_parameter_memref {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
u32 memref_handle;
|
||||
u32 unused;
|
||||
};
|
||||
|
||||
/** This structure is used for the parameter types TEEC_MEMREF_TEMP_xxx.
|
||||
*
|
||||
* The parameter is located in World Shared Memory which is established
|
||||
* for the command and torn down afterwards.
|
||||
*
|
||||
* The number of pages to share is 'size + offset' divided by the page
|
||||
* size, rounded up.
|
||||
* Inside the shared pages, the buffer starts at address 'offset'
|
||||
* and ends after 'size' bytes.
|
||||
*
|
||||
* - wsm_type parameter may be WSM_CONTIGUOUS or WSM_L1.
|
||||
* - offset must be less than the page size (4096).
|
||||
* - size must be less than 0xfffff000.
|
||||
*/
|
||||
struct interworld_parameter_tmpref {
|
||||
u16 wsm_type;
|
||||
u16 offset;
|
||||
u32 size;
|
||||
u64 physical_address;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
union interworld_parameter {
|
||||
struct interworld_parameter_value value;
|
||||
struct interworld_parameter_memref memref;
|
||||
struct interworld_parameter_tmpref tmpref;
|
||||
};
|
||||
|
||||
/**
|
||||
* An inter-world session structure represents an active session between
|
||||
* a normal world client and RTM.
|
||||
* It is located in the MCI buffer, must be 8-byte aligned
|
||||
*
|
||||
* NB : since the session structure is in shared memory, it must have the
|
||||
* same layout on both sides (normal world kernel and RTM).
|
||||
* All types use platform endianness (specifically, the endianness used by
|
||||
* the secure world).
|
||||
*/
|
||||
struct interworld_session {
|
||||
u32 status;
|
||||
u32 return_origin;
|
||||
u16 session_handle;
|
||||
u16 param_types;
|
||||
|
||||
union {
|
||||
u32 command_id; /** invoke-command only */
|
||||
u32 login; /** open-session only */
|
||||
};
|
||||
|
||||
union interworld_parameter params[4];
|
||||
|
||||
/* The following fields are only used during open-session */
|
||||
struct teec_uuid target_uuid;
|
||||
struct teec_uuid client_uuid;
|
||||
};
|
||||
|
||||
#endif /** MCIIWP_H_ */
|
||||
487
drivers/gud/MobiCoreDriver/mcimcp.h
Normal file
487
drivers/gud/MobiCoreDriver/mcimcp.h
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MCP_H_
|
||||
#define MCP_H_
|
||||
|
||||
#include "mcloadformat.h"
|
||||
|
||||
/** Indicates a response */
|
||||
#define FLAG_RESPONSE BIT(31)
|
||||
|
||||
/** MobiCore Return Code Defines.
|
||||
* List of the possible MobiCore return codes.
|
||||
*/
|
||||
enum mcp_result {
|
||||
/** Memory has successfully been mapped */
|
||||
MC_MCP_RET_OK = 0,
|
||||
/** The session ID is invalid */
|
||||
MC_MCP_RET_ERR_INVALID_SESSION = 1,
|
||||
/** The UUID of the Trustlet is unknown */
|
||||
MC_MCP_RET_ERR_UNKNOWN_UUID = 2,
|
||||
/** The ID of the driver is unknown */
|
||||
MC_MCP_RET_ERR_UNKNOWN_DRIVER_ID = 3,
|
||||
/** No more session are allowed */
|
||||
MC_MCP_RET_ERR_NO_MORE_SESSIONS = 4,
|
||||
/** The container is invalid */
|
||||
MC_MCP_RET_ERR_CONTAINER_INVALID = 5,
|
||||
/** The Trustlet is invalid */
|
||||
MC_MCP_RET_ERR_TRUSTLET_INVALID = 6,
|
||||
/** The memory block has already been mapped before */
|
||||
MC_MCP_RET_ERR_ALREADY_MAPPED = 7,
|
||||
/** Alignment or length error in the command parameters */
|
||||
MC_MCP_RET_ERR_INVALID_PARAM = 8,
|
||||
/** No space left in the virtual address space of the session */
|
||||
MC_MCP_RET_ERR_OUT_OF_RESOURCES = 9,
|
||||
/** WSM type unknown or broken WSM */
|
||||
MC_MCP_RET_ERR_INVALID_WSM = 10,
|
||||
/** unknown error */
|
||||
MC_MCP_RET_ERR_UNKNOWN = 11,
|
||||
/** Length of map invalid */
|
||||
MC_MCP_RET_ERR_INVALID_MAPPING_LENGTH = 12,
|
||||
/** Map can only be applied to Trustlet session */
|
||||
MC_MCP_RET_ERR_MAPPING_TARGET = 13,
|
||||
/** Couldn't open crypto session */
|
||||
MC_MCP_RET_ERR_OUT_OF_CRYPTO_RESOURCES = 14,
|
||||
/** System Trustlet signature verification failed */
|
||||
MC_MCP_RET_ERR_SIGNATURE_VERIFICATION_FAILED = 15,
|
||||
/** System Trustlet public key is wrong */
|
||||
MC_MCP_RET_ERR_WRONG_PUBLIC_KEY = 16,
|
||||
/** Wrong containter type(s) */
|
||||
MC_MCP_RET_ERR_CONTAINER_TYPE_MISMATCH = 17,
|
||||
/** Container is locked (or not activated) */
|
||||
MC_MCP_RET_ERR_CONTAINER_LOCKED = 18,
|
||||
/** SPID is not registered with root container */
|
||||
MC_MCP_RET_ERR_SP_NO_CHILD = 19,
|
||||
/** UUID is not registered with sp container */
|
||||
MC_MCP_RET_ERR_TL_NO_CHILD = 20,
|
||||
/** Unwrapping of root container failed */
|
||||
MC_MCP_RET_ERR_UNWRAP_ROOT_FAILED = 21,
|
||||
/** Unwrapping of service provider container failed */
|
||||
MC_MCP_RET_ERR_UNWRAP_SP_FAILED = 22,
|
||||
/** Unwrapping of Trustlet container failed */
|
||||
MC_MCP_RET_ERR_UNWRAP_TRUSTLET_FAILED = 23,
|
||||
/** Container version mismatch */
|
||||
MC_MCP_RET_ERR_CONTAINER_VERSION_MISMATCH = 24,
|
||||
/** Decryption of service provider trustlet failed */
|
||||
MC_MCP_RET_ERR_SP_TL_DECRYPTION_FAILED = 25,
|
||||
/** Hash check of service provider trustlet failed */
|
||||
MC_MCP_RET_ERR_SP_TL_HASH_CHECK_FAILED = 26,
|
||||
/** Activation/starting of task failed */
|
||||
MC_MCP_RET_ERR_LAUNCH_TASK_FAILED = 27,
|
||||
/** Closing of task not yet possible, try again later */
|
||||
MC_MCP_RET_ERR_CLOSE_TASK_FAILED = 28,
|
||||
/**< Service is blocked and a session cannot be opened to it */
|
||||
MC_MCP_RET_ERR_SERVICE_BLOCKED = 29,
|
||||
/**< Service is locked and a session cannot be opened to it */
|
||||
MC_MCP_RET_ERR_SERVICE_LOCKED = 30,
|
||||
/**< Service was forcefully killed (due to an administrative command) */
|
||||
MC_MCP_RET_ERR_SERVICE_KILLED = 31,
|
||||
/**< Service version is lower than the one installed. */
|
||||
MC_MCP_RET_ERR_DOWNGRADE_NOT_AUTHORIZED = 32,
|
||||
/**< Filesystem not yet ready. */
|
||||
MC_MCP_RET_ERR_SYSTEM_NOT_READY = 33,
|
||||
/** The command is unknown */
|
||||
MC_MCP_RET_ERR_UNKNOWN_COMMAND = 50,
|
||||
/** The command data is invalid */
|
||||
MC_MCP_RET_ERR_INVALID_DATA = 51
|
||||
};
|
||||
|
||||
/** Possible MCP Command IDs
|
||||
* Command ID must be between 0 and 0x7FFFFFFF.
|
||||
*/
|
||||
enum cmd_id {
|
||||
/** Invalid command ID */
|
||||
MC_MCP_CMD_ID_INVALID = 0x00,
|
||||
/** Open a session */
|
||||
MC_MCP_CMD_OPEN_SESSION = 0x01,
|
||||
/** Close an existing session */
|
||||
MC_MCP_CMD_CLOSE_SESSION = 0x03,
|
||||
/** Map WSM to session */
|
||||
MC_MCP_CMD_MAP = 0x04,
|
||||
/** Unmap WSM from session */
|
||||
MC_MCP_CMD_UNMAP = 0x05,
|
||||
/** Prepare for suspend */
|
||||
MC_MCP_CMD_SUSPEND = 0x06,
|
||||
/** Resume from suspension */
|
||||
MC_MCP_CMD_RESUME = 0x07,
|
||||
/** Get MobiCore version information */
|
||||
MC_MCP_CMD_GET_MOBICORE_VERSION = 0x09,
|
||||
/** Close MCP and unmap MCI */
|
||||
MC_MCP_CMD_CLOSE_MCP = 0x0A,
|
||||
/** Load token for device attestation */
|
||||
MC_MCP_CMD_LOAD_TOKEN = 0x0B,
|
||||
/** Check that TA can be loaded */
|
||||
MC_MCP_CMD_CHECK_LOAD_TA = 0x0C,
|
||||
/** Load a decryption key */
|
||||
MC_MCP_CMD_LOAD_SYSENC_KEY_SO = 0x0D,
|
||||
};
|
||||
|
||||
/*
|
||||
* Types of WSM known to the MobiCore.
|
||||
*/
|
||||
#define WSM_TYPE_MASK 0xFF
|
||||
#define WSM_INVALID 0 /** Invalid memory type */
|
||||
#define WSM_L1 3 /** Buffer mapping uses fake L1 table */
|
||||
/**< Bitflag indicating that the buffer should be uncached */
|
||||
#define WSM_UNCACHED 0x100
|
||||
|
||||
/*
|
||||
* Magic number used to identify if Open Command supports GP client
|
||||
* authentication.
|
||||
*/
|
||||
#define MC_GP_CLIENT_AUTH_MAGIC 0x47504131 /* "GPA1" */
|
||||
|
||||
/*
|
||||
* Initialisation values flags
|
||||
*/
|
||||
/* Set if IRQ is present */
|
||||
#define MC_IV_FLAG_IRQ BIT(0)
|
||||
/* Set if GP TIME is supported */
|
||||
#define MC_IV_FLAG_TIME BIT(1)
|
||||
/* Set if GP client uses interworld session */
|
||||
#define MC_IV_FLAG_IWP BIT(2)
|
||||
|
||||
struct init_values {
|
||||
u32 flags;
|
||||
u32 irq;
|
||||
u32 time_ofs;
|
||||
u32 time_len;
|
||||
/* interworld session buffer offset in MCI */
|
||||
u32 iws_buf_ofs;
|
||||
/* interworld session buffer size */
|
||||
u32 iws_buf_size;
|
||||
u8 padding[8];
|
||||
};
|
||||
|
||||
/** Command header.
|
||||
* It just contains the command ID. Only values specified in cmd_id are
|
||||
* allowed as command IDs. If the command ID is unspecified the MobiCore
|
||||
* returns an empty response with the result set to
|
||||
* MC_MCP_RET_ERR_UNKNOWN_COMMAND.
|
||||
*/
|
||||
struct cmd_header {
|
||||
enum cmd_id cmd_id; /** Command ID of the command */
|
||||
};
|
||||
|
||||
/** Response header.
|
||||
* MobiCore will reply to every MCP command with an MCP response. Like the MCP
|
||||
* command the response consists of a header followed by response data. The
|
||||
* response is written to the same memory location as the MCP command.
|
||||
*/
|
||||
struct rsp_header {
|
||||
u32 rsp_id; /** Command ID | FLAG_RESPONSE */
|
||||
enum mcp_result result; /** Result of the command execution */
|
||||
};
|
||||
|
||||
/** @defgroup CMD MCP Commands
|
||||
*/
|
||||
|
||||
/** @defgroup ASMCMD Administrative Commands
|
||||
*/
|
||||
|
||||
/** @defgroup MCPGETMOBICOREVERSION GET_MOBICORE_VERSION
|
||||
* Get MobiCore version info.
|
||||
*
|
||||
*/
|
||||
|
||||
/** Get MobiCore Version Command */
|
||||
struct cmd_get_version {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
};
|
||||
|
||||
/** Get MobiCore Version Command Response */
|
||||
struct rsp_get_version {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
struct mc_version_info version_info; /** MobiCore version info */
|
||||
};
|
||||
|
||||
/** @defgroup POWERCMD Power Management Commands
|
||||
*/
|
||||
|
||||
/** @defgroup MCPSUSPEND SUSPEND
|
||||
* Prepare MobiCore suspension.
|
||||
* This command allows MobiCore and MobiCore drivers to release or clean
|
||||
* resources and save device state.
|
||||
*
|
||||
*/
|
||||
|
||||
/** Suspend Command */
|
||||
struct cmd_suspend {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
};
|
||||
|
||||
/** Suspend Command Response */
|
||||
struct rsp_suspend {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** @defgroup MCPRESUME RESUME
|
||||
* Resume MobiCore from suspension.
|
||||
* This command allows MobiCore and MobiCore drivers to reinitialize hardware
|
||||
* affected by suspension.
|
||||
*
|
||||
*/
|
||||
|
||||
/** Resume Command */
|
||||
struct cmd_resume {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
};
|
||||
|
||||
/** Resume Command Response */
|
||||
struct rsp_resume {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** @defgroup SESSCMD Session Management Commands
|
||||
*/
|
||||
|
||||
/** @defgroup MCPOPEN OPEN
|
||||
* Load and open a session to a Trustlet.
|
||||
* The OPEN command loads Trustlet data to the MobiCore context and opens a
|
||||
* session to the Trustlet. If wsm_data_type is WSM_INVALID MobiCore tries to
|
||||
* start a pre-installed Trustlet associated with the uuid passed. The uuid
|
||||
* passed must match the uuid contained in the load data (if available).
|
||||
* On success, MobiCore returns the session ID which can be used for further
|
||||
* communication.
|
||||
*/
|
||||
|
||||
/** GP client authentication data */
|
||||
struct cmd_open_data {
|
||||
u32 mclf_magic; /** ASCII "MCLF" on older versions */
|
||||
struct identity identity; /** Login method and data */
|
||||
};
|
||||
|
||||
/** Open Command */
|
||||
struct cmd_open {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
struct mc_uuid_t uuid; /** Service UUID */
|
||||
u8 unused[4]; /** Padding to be 64-bit aligned */
|
||||
u64 adr_tci_buffer; /** Physical address of the TCI MMU */
|
||||
u64 adr_load_data; /** Physical address of the data MMU */
|
||||
u32 ofs_tci_buffer; /** Offset to the data */
|
||||
u32 len_tci_buffer; /** Length of the TCI */
|
||||
u32 wsmtype_tci; /** Type of WSM used for the TCI */
|
||||
u32 wsm_data_type; /** Type of MMU */
|
||||
u32 ofs_load_data; /** Offset to the data */
|
||||
u32 len_load_data; /** Length of the data to load */
|
||||
union {
|
||||
struct cmd_open_data cmd_open_data; /** Client login data */
|
||||
union mclf_header tl_header; /** Service header */
|
||||
};
|
||||
u32 is_gpta; /** true if looking for an SD/GP-TA */
|
||||
};
|
||||
|
||||
/** Open Command Response */
|
||||
struct rsp_open {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
u32 session_id; /** Session ID */
|
||||
};
|
||||
|
||||
/** TA Load Check Command */
|
||||
struct cmd_check_load {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
struct mc_uuid_t uuid; /** Service UUID */
|
||||
u8 unused[4]; /** Padding to be 64-bit aligned */
|
||||
u64 adr_load_data; /** Physical address of the data */
|
||||
u32 wsm_data_type; /** Type of MMU */
|
||||
u32 ofs_load_data; /** Offset to the data */
|
||||
u32 len_load_data; /** Length of the data to load */
|
||||
union mclf_header tl_header; /** Service header */
|
||||
};
|
||||
|
||||
/** TA Load Check Response */
|
||||
struct rsp_check_load {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** @defgroup MCPCLOSE CLOSE
|
||||
* Close an existing session to a Trustlet.
|
||||
* The CLOSE command terminates a session and frees all resources in the
|
||||
* MobiCore system which are currently occupied by the session. Before closing
|
||||
* the session, the MobiCore runtime management waits until all pending
|
||||
* operations, like calls to drivers, invoked by the Trustlet have been
|
||||
* terminated. Mapped memory will automatically be unmapped from the MobiCore
|
||||
* context. The NWd is responsible for processing the freed memory according to
|
||||
* the Rich-OS needs.
|
||||
*
|
||||
*/
|
||||
|
||||
/** Close Command */
|
||||
struct cmd_close {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
u32 session_id; /** Session ID */
|
||||
};
|
||||
|
||||
/** Close Command Response */
|
||||
struct rsp_close {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** @defgroup MCPMAP MAP
|
||||
* Map a portion of memory to a session.
|
||||
* The MAP command provides a block of memory to the context of a service.
|
||||
* The memory then becomes world-shared memory (WSM).
|
||||
* The only allowed memory type here is WSM_L1.
|
||||
*/
|
||||
|
||||
/** Map Command */
|
||||
struct cmd_map {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
u32 session_id; /** Session ID */
|
||||
u32 wsm_type; /** Type of MMU */
|
||||
u32 ofs_buffer; /** Offset to the payload */
|
||||
u64 adr_buffer; /** Physical address of the MMU */
|
||||
u32 len_buffer; /** Length of the buffer */
|
||||
u32 flags; /** Attributes (read/write) */
|
||||
};
|
||||
|
||||
#define MCP_MAP_MAX 0x100000 /** Maximum length for MCP map */
|
||||
|
||||
/** Map Command Response */
|
||||
struct rsp_map {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
/** Virtual address the WSM is mapped to, may include an offset! */
|
||||
u32 secure_va;
|
||||
};
|
||||
|
||||
/** @defgroup MCPUNMAP UNMAP
|
||||
* Unmap a portion of world-shared memory from a session.
|
||||
* The UNMAP command is used to unmap a previously mapped block of
|
||||
* world shared memory from the context of a session.
|
||||
*
|
||||
* Attention: The memory block will be immediately unmapped from the specified
|
||||
* session. If the service is still accessing the memory, the service will
|
||||
* trigger a segmentation fault.
|
||||
*/
|
||||
|
||||
/** Unmap Command */
|
||||
struct cmd_unmap {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
u32 session_id; /** Session ID */
|
||||
u32 wsm_type; /** Type of WSM used of the memory */
|
||||
/** Virtual address the WSM is mapped to, may include an offset! */
|
||||
u32 secure_va;
|
||||
u32 virtual_buffer_len; /** Length of virtual buffer */
|
||||
};
|
||||
|
||||
/** Unmap Command Response */
|
||||
struct rsp_unmap {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** @defgroup MCPLOADTOKEN
|
||||
* Load a token from the normal world and share it with the TEE
|
||||
* If something fails, the device attestation functionality will be disabled
|
||||
*/
|
||||
|
||||
/** Load Token */
|
||||
struct cmd_load_token {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
u32 wsm_data_type; /** Type of MMU */
|
||||
u64 adr_load_data; /** Physical address of the MMU */
|
||||
u64 ofs_load_data; /** Offset to the data */
|
||||
u64 len_load_data; /** Length of the data */
|
||||
};
|
||||
|
||||
/** Load Token Command Response */
|
||||
struct rsp_load_token {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** @defgroup MCPLOADKEYSO
|
||||
* Load a key SO from the normal world and share it with the TEE
|
||||
* If something fails, the device attestation functionality will be disabled
|
||||
*/
|
||||
|
||||
/** Load key SO */
|
||||
struct cmd_load_key_so {
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
u32 wsm_data_type; /** Type of MMU */
|
||||
u64 adr_load_data; /** Physical address of the MMU */
|
||||
u64 ofs_load_data; /** Offset to the data */
|
||||
u64 len_load_data; /** Length of the data */
|
||||
};
|
||||
|
||||
/** Load key SO Command Response */
|
||||
struct rsp_load_key_so {
|
||||
struct rsp_header rsp_header; /** Response header */
|
||||
};
|
||||
|
||||
/** Structure of the MCP buffer */
|
||||
union mcp_message {
|
||||
struct init_values init_values; /** Initialisation values */
|
||||
struct cmd_header cmd_header; /** Command header */
|
||||
struct rsp_header rsp_header;
|
||||
struct cmd_open cmd_open; /** Load and open service */
|
||||
struct rsp_open rsp_open;
|
||||
struct cmd_close cmd_close; /** Close command */
|
||||
struct rsp_close rsp_close;
|
||||
struct cmd_map cmd_map; /** Map WSM to service */
|
||||
struct rsp_map rsp_map;
|
||||
struct cmd_unmap cmd_unmap; /** Unmap WSM from service */
|
||||
struct rsp_unmap rsp_unmap;
|
||||
struct cmd_suspend cmd_suspend; /** Suspend MobiCore */
|
||||
struct rsp_suspend rsp_suspend;
|
||||
struct cmd_resume cmd_resume; /** Resume MobiCore */
|
||||
struct rsp_resume rsp_resume;
|
||||
struct cmd_get_version cmd_get_version; /** Get MobiCore Version */
|
||||
struct rsp_get_version rsp_get_version;
|
||||
struct cmd_load_token cmd_load_token; /** Load token */
|
||||
struct rsp_load_token rsp_load_token;
|
||||
struct cmd_check_load cmd_check_load; /** TA load check */
|
||||
struct rsp_check_load rsp_check_load;
|
||||
struct cmd_load_key_so cmd_load_key_so;/** Load key SO */
|
||||
struct rsp_load_key_so rsp_load_key_so;
|
||||
};
|
||||
|
||||
#define MC_FLAG_NO_SLEEP_REQ 0
|
||||
#define MC_FLAG_REQ_TO_SLEEP 1
|
||||
|
||||
#define MC_STATE_NORMAL_EXECUTION 0
|
||||
#define MC_STATE_READY_TO_SLEEP 1
|
||||
|
||||
#define MC_STATE_FLAG_TEE_HALT_MASK BIT(0)
|
||||
|
||||
struct sleep_mode {
|
||||
u16 sleep_req; /** Ask SWd to get ready to sleep */
|
||||
u16 ready_to_sleep; /** SWd is now ready to sleep */
|
||||
};
|
||||
|
||||
/** MobiCore status flags */
|
||||
struct mcp_flags {
|
||||
/** If not MC_FLAG_SCHEDULE_IDLE, MobiCore needs scheduling */
|
||||
u32 schedule;
|
||||
struct sleep_mode sleep_mode;
|
||||
/** Secure-world sleep timeout in milliseconds */
|
||||
s32 timeout_ms;
|
||||
/** TEE flags */
|
||||
u8 tee_flags;
|
||||
/** Reserved for future use */
|
||||
u8 RFU_padding[3];
|
||||
};
|
||||
|
||||
/** MobiCore is idle. No scheduling required */
|
||||
#define MC_FLAG_SCHEDULE_IDLE 0
|
||||
/** MobiCore is non idle, scheduling is required */
|
||||
#define MC_FLAG_SCHEDULE_NON_IDLE 1
|
||||
|
||||
/** MCP buffer structure */
|
||||
struct mcp_buffer {
|
||||
struct mcp_flags flags; /** MobiCore Flags */
|
||||
union mcp_message message; /** MCP message buffer */
|
||||
};
|
||||
|
||||
#endif /* MCP_H_ */
|
||||
91
drivers/gud/MobiCoreDriver/mcinq.h
Normal file
91
drivers/gud/MobiCoreDriver/mcinq.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef NQ_H_
|
||||
#define NQ_H_
|
||||
|
||||
/** \name NQ Size Defines
|
||||
* Minimum and maximum count of elements in the notification queue.
|
||||
*/
|
||||
#define MIN_NQ_ELEM 1 /** Minimum notification queue elements */
|
||||
#define MAX_NQ_ELEM 64 /** Maximum notification queue elements */
|
||||
|
||||
/* Compute notification queue size in bytes from its number of elements */
|
||||
#define NQ_SIZE(n) (2 * (sizeof(struct notification_queue_header)\
|
||||
+ (n) * sizeof(struct notification)))
|
||||
|
||||
/** \name NQ Length Defines
|
||||
* Note that there is one queue for NWd->SWd and one queue for SWd->NWd
|
||||
*/
|
||||
/** Minimum size for the notification queue data structure */
|
||||
#define MIN_NQ_LEN NQ_SIZE(MIN_NQ_ELEM)
|
||||
/** Maximum size for the notification queue data structure */
|
||||
#define MAX_NQ_LEN NQ_SIZE(MAX_NQ_ELEM)
|
||||
|
||||
/** \name Session ID Defines
|
||||
* Standard Session IDs.
|
||||
*/
|
||||
/** MCP session ID, used to communicate with MobiCore (e.g. to start/stop TA) */
|
||||
#define SID_MCP 0
|
||||
/** Invalid session id, returned in case of error */
|
||||
#define SID_INVALID 0xffffffff
|
||||
|
||||
/** Notification data structure */
|
||||
struct notification {
|
||||
u32 session_id; /** Session ID */
|
||||
s32 payload; /** Additional notification info */
|
||||
};
|
||||
|
||||
/** Notification payload codes.
|
||||
* 0 indicated a plain simple notification,
|
||||
* a positive value is a termination reason from the task,
|
||||
* a negative value is a termination reason from MobiCore.
|
||||
* Possible negative values are given below.
|
||||
*/
|
||||
enum notification_payload {
|
||||
/** task terminated, but exit code is invalid */
|
||||
ERR_INVALID_EXIT_CODE = -1,
|
||||
/** task terminated due to session end, no exit code available */
|
||||
ERR_SESSION_CLOSE = -2,
|
||||
/** task terminated due to invalid operation */
|
||||
ERR_INVALID_OPERATION = -3,
|
||||
/** session ID is unknown */
|
||||
ERR_INVALID_SID = -4,
|
||||
/** session is not active */
|
||||
ERR_SID_NOT_ACTIVE = -5,
|
||||
/** session was force-killed (due to an administrative command). */
|
||||
ERR_SESSION_KILLED = -6,
|
||||
};
|
||||
|
||||
/** Declaration of the notification queue header.
|
||||
* layout as specified in the data structure specification.
|
||||
*/
|
||||
struct notification_queue_header {
|
||||
u32 write_cnt; /** Write counter */
|
||||
u32 read_cnt; /** Read counter */
|
||||
u32 queue_size; /** Queue size */
|
||||
};
|
||||
|
||||
/** Queue struct which defines a queue object.
|
||||
* The queue struct is accessed by the queue<operation> type of
|
||||
* function. elementCnt must be a power of two and the power needs
|
||||
* to be smaller than power of u32 (obviously 32).
|
||||
*/
|
||||
struct notification_queue {
|
||||
struct notification_queue_header hdr; /** Queue header */
|
||||
struct notification notification[MIN_NQ_ELEM]; /** Elements */
|
||||
};
|
||||
|
||||
#endif /** NQ_H_ */
|
||||
31
drivers/gud/MobiCoreDriver/mcitime.h
Normal file
31
drivers/gud/MobiCoreDriver/mcitime.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MCITIME_H_
|
||||
#define MCITIME_H_
|
||||
|
||||
/*
|
||||
* Trustonic TEE RICH OS Time:
|
||||
* -seconds and nanoseconds since Jan 1, 1970, UTC
|
||||
* -monotonic counter
|
||||
*/
|
||||
struct mcp_time {
|
||||
u64 wall_clock_seconds;
|
||||
u64 wall_clock_nsec;
|
||||
u64 monotonic_seconds;
|
||||
u64 monotonic_nsec;
|
||||
};
|
||||
|
||||
#endif /* MCITIME_H_ */
|
||||
143
drivers/gud/MobiCoreDriver/mcloadformat.h
Normal file
143
drivers/gud/MobiCoreDriver/mcloadformat.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MCLOADFORMAT_H_
|
||||
#define MCLOADFORMAT_H_
|
||||
|
||||
/** Trustlet Blob length info */
|
||||
#define MC_TLBLOBLEN_MAGIC 0x7672746C /* Magic for SWd: vrtl */
|
||||
#define MAX_SO_CONT_SIZE 512 /* Max size for a container */
|
||||
|
||||
/** MCLF magic */
|
||||
/**< "MCLF" in big endian integer representation */
|
||||
#define MC_SERVICE_HEADER_MAGIC_BE \
|
||||
((uint32_t)('M' | ('C' << 8) | ('L' << 16) | ('F' << 24)))
|
||||
/**< "MCLF" in little endian integer representation */
|
||||
#define MC_SERVICE_HEADER_MAGIC_LE \
|
||||
((uint32_t)(('M' << 24) | ('C' << 16) | ('L' << 8) | 'F'))
|
||||
|
||||
/** MCLF flags */
|
||||
/**< Loaded service cannot be unloaded from MobiCore. */
|
||||
#define MC_SERVICE_HEADER_FLAGS_PERMANENT BIT(0)
|
||||
/**< Service has no WSM control interface. */
|
||||
#define MC_SERVICE_HEADER_FLAGS_NO_CONTROL_INTERFACE BIT(1)
|
||||
/**< Service can be debugged. */
|
||||
#define MC_SERVICE_HEADER_FLAGS_DEBUGGABLE BIT(2)
|
||||
/**< New-layout trusted application or trusted driver. */
|
||||
#define MC_SERVICE_HEADER_FLAGS_EXTENDED_LAYOUT BIT(3)
|
||||
|
||||
/** Service type.
|
||||
* The service type defines the type of executable.
|
||||
*/
|
||||
enum service_type {
|
||||
SERVICE_TYPE_ILLEGAL = 0,
|
||||
SERVICE_TYPE_DRIVER = 1,
|
||||
SERVICE_TYPE_SP_TRUSTLET = 2,
|
||||
SERVICE_TYPE_SYSTEM_TRUSTLET = 3,
|
||||
SERVICE_TYPE_MIDDLEWARE = 4,
|
||||
SERVICE_TYPE_LAST_ENTRY = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* Descriptor for a memory segment.
|
||||
*/
|
||||
struct segment_descriptor {
|
||||
u32 start; /**< Virtual start address */
|
||||
u32 len; /**< Segment length in bytes */
|
||||
};
|
||||
|
||||
/**
|
||||
* MCLF intro for data structure identification.
|
||||
* Must be the first element of a valid MCLF file.
|
||||
*/
|
||||
struct mclf_intro {
|
||||
u32 magic; /**< Header magic value ASCII "MCLF" */
|
||||
u32 version; /**< Version the MCLF header struct */
|
||||
};
|
||||
|
||||
/**
|
||||
* @defgroup MCLF_VER_V2 MCLF Version 32
|
||||
* @ingroup MCLF_VER
|
||||
*
|
||||
* @addtogroup MCLF_VER_V2
|
||||
*/
|
||||
|
||||
/*
|
||||
* GP TA identity.
|
||||
*/
|
||||
struct identity {
|
||||
/**< GP TA login type */
|
||||
u32 login_type;
|
||||
/**< GP TA login data */
|
||||
u8 login_data[16];
|
||||
};
|
||||
|
||||
/**
|
||||
* Version 2.1/2.2 MCLF header.
|
||||
*/
|
||||
struct mclf_header_v2 {
|
||||
/**< MCLF header start with the mandatory intro */
|
||||
struct mclf_intro intro;
|
||||
/**< Service flags */
|
||||
u32 flags;
|
||||
/**< Type of memory the service must be executed from */
|
||||
u32 mem_type;
|
||||
/**< Type of service */
|
||||
enum service_type service_type;
|
||||
/**< Number of instances which can be run simultaneously */
|
||||
u32 num_instances;
|
||||
/**< Loadable service unique identifier (UUID) */
|
||||
struct mc_uuid_t uuid;
|
||||
/**< If the service_type is SERVICE_TYPE_DRIVER the Driver ID is used */
|
||||
u32 driver_id;
|
||||
/**<
|
||||
* Number of threads (N) in a service:
|
||||
* SERVICE_TYPE_SP_TRUSTLET: N = 1
|
||||
* SERVICE_TYPE_SYSTEM_TRUSTLET: N = 1
|
||||
* SERVICE_TYPE_DRIVER: N >= 1
|
||||
*/
|
||||
u32 num_threads;
|
||||
/**< Virtual text segment */
|
||||
struct segment_descriptor text;
|
||||
/**< Virtual data segment */
|
||||
struct segment_descriptor data;
|
||||
/**< Length of the BSS segment in bytes. MUST be at least 8 byte */
|
||||
u32 bss_len;
|
||||
/**< Virtual start address of service code */
|
||||
u32 entry;
|
||||
/**< Version of the interface the driver exports */
|
||||
u32 service_version;
|
||||
};
|
||||
|
||||
/**
|
||||
* @addtogroup MCLF
|
||||
*/
|
||||
|
||||
/** MCLF header */
|
||||
union mclf_header {
|
||||
/**< Intro for data identification */
|
||||
struct mclf_intro intro;
|
||||
/**< Version 2 header */
|
||||
struct mclf_header_v2 mclf_header_v2;
|
||||
};
|
||||
|
||||
struct mc_blob_len_info {
|
||||
u32 magic; /**< New blob format magic number */
|
||||
u32 root_size; /**< Root container size */
|
||||
u32 sp_size; /**< SP container size */
|
||||
u32 ta_size; /**< TA container size */
|
||||
u32 reserved[4]; /**< Reserved for further Use */
|
||||
};
|
||||
|
||||
#endif /* MCLOADFORMAT_H_ */
|
||||
1038
drivers/gud/MobiCoreDriver/mcp.c
Normal file
1038
drivers/gud/MobiCoreDriver/mcp.c
Normal file
File diff suppressed because it is too large
Load diff
114
drivers/gud/MobiCoreDriver/mcp.h
Normal file
114
drivers/gud/MobiCoreDriver/mcp.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_MCP_H_
|
||||
#define _MC_MCP_H_
|
||||
|
||||
#include "mcloadformat.h" /* struct identity */
|
||||
#include "nq.h"
|
||||
|
||||
struct tee_mmu;
|
||||
|
||||
/* Structure to hold the TA/driver information at open */
|
||||
struct mcp_open_info {
|
||||
enum {
|
||||
TEE_MC_UUID,
|
||||
TEE_MC_TA,
|
||||
TEE_MC_DRIVER,
|
||||
TEE_MC_DRIVER_UUID,
|
||||
} type;
|
||||
/* TA/driver */
|
||||
const struct mc_uuid_t *uuid;
|
||||
u32 spid;
|
||||
uintptr_t va;
|
||||
size_t len;
|
||||
/* TCI */
|
||||
uintptr_t tci_va;
|
||||
size_t tci_len;
|
||||
struct tee_mmu *tci_mmu;
|
||||
/* Origin */
|
||||
int user;
|
||||
};
|
||||
|
||||
/* Structure to hold the TA/driver descriptor to pass to MCP */
|
||||
struct tee_object {
|
||||
u32 length; /* Total length */
|
||||
u32 header_length; /* Length of header before payload */
|
||||
u8 data[]; /* Header followed by payload */
|
||||
};
|
||||
|
||||
/* Structure to hold all mapped buffer data to pass to MCP */
|
||||
struct mcp_buffer_map {
|
||||
u64 addr; /** Page-aligned PA, or VA */
|
||||
unsigned long nr_pages; /** Total number of pages mapped */
|
||||
u32 secure_va; /** SWd virtual address */
|
||||
u32 offset; /** Data offset inside the first page */
|
||||
u32 length; /** Length of the data */
|
||||
u32 type; /** Type of MMU */
|
||||
u32 flags; /** Flags (typically read/write) */
|
||||
struct tee_mmu *mmu; /** MMU from which the map was made */
|
||||
};
|
||||
|
||||
struct mcp_session {
|
||||
/* Notification queue session */
|
||||
struct nq_session nq_session;
|
||||
/* Session ID */
|
||||
u32 sid;
|
||||
/* Sessions list (protected by mcp sessions_lock) */
|
||||
struct list_head list;
|
||||
/* Notification waiter lock */
|
||||
struct mutex notif_wait_lock; /* Only one at a time */
|
||||
/* Notification received */
|
||||
struct completion completion;
|
||||
/* Notification lock */
|
||||
struct mutex exit_code_lock;
|
||||
/* Last notification */
|
||||
s32 exit_code;
|
||||
/* Session state (protected by mcp sessions_lock) */
|
||||
enum mcp_session_state {
|
||||
MCP_SESSION_RUNNING,
|
||||
MCP_SESSION_CLOSE_FAILED,
|
||||
MCP_SESSION_CLOSED,
|
||||
} state;
|
||||
/* Notification counter */
|
||||
u32 notif_count;
|
||||
};
|
||||
|
||||
/* Init for the mcp_session structure */
|
||||
void mcp_session_init(struct mcp_session *session);
|
||||
|
||||
/* Commands */
|
||||
int mcp_get_version(struct mc_version_info *version_info);
|
||||
int mcp_load_token(uintptr_t data, const struct mcp_buffer_map *buffer_map);
|
||||
int mcp_load_check(const struct tee_object *obj,
|
||||
const struct mcp_buffer_map *buffer_map);
|
||||
int mcp_load_key_so(uintptr_t data, const struct mcp_buffer_map *buffer_map);
|
||||
int mcp_open_session(struct mcp_session *session, struct mcp_open_info *info,
|
||||
bool *tci_in_use);
|
||||
int mcp_close_session(struct mcp_session *session);
|
||||
void mcp_cleanup_session(struct mcp_session *session);
|
||||
int mcp_map(u32 session_id, struct tee_mmu *mmu, u32 *sva);
|
||||
int mcp_unmap(u32 session_id, const struct mcp_buffer_map *map);
|
||||
int mcp_notify(struct mcp_session *mcp_session);
|
||||
int mcp_wait(struct mcp_session *session, s32 timeout, int silent_expiry);
|
||||
int mcp_get_err(struct mcp_session *session, s32 *err);
|
||||
|
||||
/* Initialisation/cleanup */
|
||||
int mcp_init(void);
|
||||
void mcp_exit(void);
|
||||
int mcp_start(void);
|
||||
void mcp_stop(void);
|
||||
|
||||
#endif /* _MC_MCP_H_ */
|
||||
754
drivers/gud/MobiCoreDriver/mmu.c
Normal file
754
drivers/gud/MobiCoreDriver/mmu.c
Normal file
|
|
@ -0,0 +1,754 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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 <asm/pgtable.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/version.h>
|
||||
#ifdef CONFIG_DMA_SHARED_BUFFER
|
||||
#include <linux/dma-buf.h>
|
||||
#endif
|
||||
#ifdef CONFIG_ION
|
||||
#ifdef CONFIG_ION_SYSTEM_HEAP /* Android kernel only */
|
||||
#if KERNEL_VERSION(5, 4, 0) < LINUX_VERSION_CODE
|
||||
#include <linux/ion.h>
|
||||
#elif KERNEL_VERSION(4, 11, 12) < LINUX_VERSION_CODE
|
||||
#include "../../drivers/staging/android/ion/ion.h"
|
||||
#elif KERNEL_VERSION(3, 14, 0) < LINUX_VERSION_CODE
|
||||
#include "../../drivers/staging/android/ion/ion_priv.h"
|
||||
#endif /* KERNEL_VERSION */
|
||||
#else /* CONFIG_ION_SYSTEM_HEAP */
|
||||
#if KERNEL_VERSION(5, 4, 0) < LINUX_VERSION_CODE
|
||||
#include "../../drivers/staging/android/ion/ion.h"
|
||||
#elif KERNEL_VERSION(4, 11, 12) < LINUX_VERSION_CODE
|
||||
#include "../../drivers/staging/android/ion/ion.h"
|
||||
#elif KERNEL_VERSION(3, 14, 0) < LINUX_VERSION_CODE
|
||||
/* very old Android kernel without ION_SYSTEM_HEAP falls here */
|
||||
#include "../../drivers/staging/android/ion/ion_priv.h"
|
||||
#endif
|
||||
#endif /* CONFIG_ION_SYSTEM_HEAP */
|
||||
#endif /* CONFIG_ION */
|
||||
|
||||
#ifdef CONFIG_XEN
|
||||
/* To get the MFN */
|
||||
#include <linux/pfn.h>
|
||||
#include <xen/page.h>
|
||||
#endif
|
||||
|
||||
#include "mc_user.h"
|
||||
|
||||
#include "mcimcp.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "mcp.h" /* mcp_buffer_map */
|
||||
#include "mmu.h"
|
||||
|
||||
#define PHYS_48BIT_MASK (BIT(48) - 1)
|
||||
|
||||
/* Common */
|
||||
#define MMU_BUFFERABLE BIT(2) /* AttrIndx[0] */
|
||||
#define MMU_CACHEABLE BIT(3) /* AttrIndx[1] */
|
||||
#define MMU_EXT_NG BIT(11) /* ARMv6 and higher */
|
||||
|
||||
/* LPAE */
|
||||
#define MMU_TYPE_PAGE (3 << 0)
|
||||
#define MMU_NS BIT(5)
|
||||
#define MMU_AP_RW_ALL BIT(6) /* AP[2:1], RW, at any privilege level */
|
||||
#define MMU_AP2_RO BIT(7)
|
||||
#define MMU_EXT_SHARED_64 (3 << 8) /* SH[1:0], inner shareable */
|
||||
#define MMU_EXT_AF BIT(10) /* Access Flag */
|
||||
#define MMU_EXT_XN (((u64)1) << 54) /* XN */
|
||||
|
||||
/* Non-LPAE */
|
||||
#define MMU_TYPE_EXT (3 << 0) /* v5 */
|
||||
#define MMU_TYPE_SMALL (2 << 0)
|
||||
#define MMU_EXT_AP0 BIT(4)
|
||||
#define MMU_EXT_AP1 (2 << 4)
|
||||
#define MMU_EXT_AP2 BIT(9)
|
||||
#define MMU_EXT_TEX(x) ((x) << 6) /* v5 */
|
||||
#define MMU_EXT_SHARED_32 BIT(10) /* ARMv6 and higher */
|
||||
|
||||
/* ION */
|
||||
/* Trustonic Specific flag to detect ION mem */
|
||||
#define MMU_ION_BUF BIT(24)
|
||||
|
||||
/*
|
||||
* Specific case for kernel 4.4.168 that does not have the same
|
||||
* get_user_pages() implementation
|
||||
*/
|
||||
#if KERNEL_VERSION(4, 4, 167) < LINUX_VERSION_CODE && \
|
||||
KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
unsigned int gup_flags = 0;
|
||||
|
||||
if (write)
|
||||
gup_flags |= FOLL_WRITE;
|
||||
|
||||
return get_user_pages(NULL, mm, start, nr_pages, gup_flags, pages,
|
||||
NULL);
|
||||
}
|
||||
#elif KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
return get_user_pages(NULL, mm, start, nr_pages, write, 0, pages, NULL);
|
||||
}
|
||||
#elif KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
unsigned int gup_flags = 0;
|
||||
|
||||
if (write)
|
||||
gup_flags |= FOLL_WRITE;
|
||||
/* gup_flags |= FOLL_CMA; */
|
||||
|
||||
return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags,
|
||||
0, pages, NULL);
|
||||
}
|
||||
#elif KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
unsigned int gup_flags = 0;
|
||||
|
||||
if (write)
|
||||
gup_flags |= FOLL_WRITE;
|
||||
|
||||
return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags,
|
||||
pages, NULL);
|
||||
}
|
||||
#elif KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
unsigned int gup_flags = 0;
|
||||
|
||||
if (write)
|
||||
gup_flags |= FOLL_WRITE;
|
||||
|
||||
return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags,
|
||||
pages, NULL, NULL);
|
||||
}
|
||||
#elif KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
unsigned int gup_flags = 0;
|
||||
|
||||
gup_flags |= FOLL_LONGTERM;
|
||||
if (write)
|
||||
gup_flags |= FOLL_WRITE;
|
||||
|
||||
return get_user_pages(start, nr_pages, gup_flags, pages, NULL);
|
||||
}
|
||||
#else
|
||||
static inline long gup_local(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
unsigned int gup_flags = 0;
|
||||
|
||||
gup_flags |= FOLL_LONGTERM;
|
||||
if (write)
|
||||
gup_flags |= FOLL_WRITE;
|
||||
|
||||
return pin_user_pages(start, nr_pages, gup_flags, pages, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline long gup_local_repeat(struct mm_struct *mm, uintptr_t start,
|
||||
unsigned long nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
int retries = 10;
|
||||
long ret = 0;
|
||||
|
||||
while (retries--) {
|
||||
ret = gup_local(mm, start, nr_pages, write, pages);
|
||||
|
||||
if (-EBUSY != ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* A table that could be either a pmd or pte
|
||||
*/
|
||||
union mmu_table {
|
||||
u64 *entries; /* Array of PTEs */
|
||||
/* Array of pages */
|
||||
struct page **pages;
|
||||
/* Array of VAs */
|
||||
uintptr_t *vas;
|
||||
/* Address of table */
|
||||
void *addr;
|
||||
/* Page for table */
|
||||
unsigned long page;
|
||||
};
|
||||
|
||||
/*
|
||||
* MMU table allocated to the Daemon or a TLC describing a world shared
|
||||
* buffer.
|
||||
* When users map a malloc()ed area into SWd, a MMU table is allocated.
|
||||
* In addition, the area of maximum 1MB virtual address space is mapped into
|
||||
* the MMU table and a handle for this table is returned to the user.
|
||||
*/
|
||||
struct tee_mmu {
|
||||
struct kref kref;
|
||||
/* Array of pages that hold buffer ptes*/
|
||||
union mmu_table pte_tables[PMD_ENTRIES_MAX];
|
||||
/* Actual number of ptes tables */
|
||||
size_t nr_pmd_entries;
|
||||
/* Contains phys @ of ptes tables */
|
||||
union mmu_table pmd_table;
|
||||
struct tee_deleter *deleter; /* Xen map to free */
|
||||
unsigned long nr_pages;
|
||||
int pages_created; /* Leak check */
|
||||
int pages_locked; /* Leak check */
|
||||
u32 offset;
|
||||
u32 length;
|
||||
u32 flags;
|
||||
/* Pages are from user space */
|
||||
bool user;
|
||||
bool use_pages_and_vas;
|
||||
/* ION case only */
|
||||
struct dma_buf *dma_buf;
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
};
|
||||
|
||||
static void tee_mmu_delete(struct tee_mmu *mmu)
|
||||
{
|
||||
unsigned long chunk, nr_pages_left = mmu->nr_pages;
|
||||
|
||||
#ifdef CONFIG_DMA_SHARED_BUFFER
|
||||
if (mmu->dma_buf) {
|
||||
dma_buf_unmap_attachment(mmu->attach, mmu->sgt,
|
||||
DMA_BIDIRECTIONAL);
|
||||
dma_buf_detach(mmu->dma_buf, mmu->attach);
|
||||
dma_buf_put(mmu->dma_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Release all locked user space pages */
|
||||
for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) {
|
||||
union mmu_table *pte_table = &mmu->pte_tables[chunk];
|
||||
unsigned long nr_pages = nr_pages_left;
|
||||
|
||||
if (nr_pages > PTE_ENTRIES_MAX)
|
||||
nr_pages = PTE_ENTRIES_MAX;
|
||||
|
||||
nr_pages_left -= nr_pages;
|
||||
|
||||
if (!pte_table->page)
|
||||
break;
|
||||
|
||||
if (mmu->user && mmu->use_pages_and_vas) {
|
||||
struct page **page = pte_table->pages;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pages; i++, page++)
|
||||
put_page(*page);
|
||||
|
||||
mmu->pages_locked -= nr_pages;
|
||||
} else if (mmu->user) {
|
||||
u64 *pte64 = pte_table->entries;
|
||||
pte_t pte;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
#if (KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE) || defined(CONFIG_ARM)
|
||||
{
|
||||
pte = *pte64++;
|
||||
/* Unused entries are 0 */
|
||||
if (!pte)
|
||||
break;
|
||||
}
|
||||
#else
|
||||
{
|
||||
pte.pte = *pte64++;
|
||||
/* Unused entries are 0 */
|
||||
if (!pte.pte)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* pte_page() cannot return NULL */
|
||||
#if KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE
|
||||
put_page(pte_page(pte));
|
||||
#else
|
||||
unpin_user_page(pte_page(pte));
|
||||
#endif
|
||||
}
|
||||
|
||||
mmu->pages_locked -= nr_pages;
|
||||
}
|
||||
|
||||
free_page(pte_table->page);
|
||||
mmu->pages_created--;
|
||||
}
|
||||
|
||||
if (mmu->pmd_table.page) {
|
||||
free_page(mmu->pmd_table.page);
|
||||
mmu->pages_created--;
|
||||
}
|
||||
|
||||
if (mmu->pages_created || mmu->pages_locked)
|
||||
mc_dev_err(-EUCLEAN,
|
||||
"leak detected: still in use %d, still locked %d",
|
||||
mmu->pages_created, mmu->pages_locked);
|
||||
|
||||
if (mmu->deleter)
|
||||
mmu->deleter->delete(mmu->deleter->object);
|
||||
|
||||
kfree(mmu);
|
||||
|
||||
/* Decrement debug counter */
|
||||
atomic_dec(&g_ctx.c_mmus);
|
||||
}
|
||||
|
||||
static struct tee_mmu *tee_mmu_create_common(const struct mcp_buffer_map *b_map)
|
||||
{
|
||||
struct tee_mmu *mmu;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (b_map->nr_pages > (PMD_ENTRIES_MAX * PTE_ENTRIES_MAX)) {
|
||||
ret = -EINVAL;
|
||||
mc_dev_err(ret, "data mapping exceeds %d pages: %lu",
|
||||
PMD_ENTRIES_MAX * PTE_ENTRIES_MAX, b_map->nr_pages);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Allocate the struct */
|
||||
mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
|
||||
if (!mmu)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Increment debug counter */
|
||||
atomic_inc(&g_ctx.c_mmus);
|
||||
kref_init(&mmu->kref);
|
||||
|
||||
/* The Xen front-end does not use PTEs */
|
||||
if (is_xen_domu())
|
||||
mmu->use_pages_and_vas = true;
|
||||
|
||||
/* Buffer info */
|
||||
mmu->offset = b_map->offset;
|
||||
mmu->length = b_map->length;
|
||||
mmu->flags = b_map->flags;
|
||||
|
||||
/* Pages info */
|
||||
mmu->nr_pages = b_map->nr_pages;
|
||||
mmu->nr_pmd_entries = (mmu->nr_pages + PTE_ENTRIES_MAX - 1) /
|
||||
PTE_ENTRIES_MAX;
|
||||
mc_dev_devel("mmu->nr_pages %lu num_ptes_pages %zu",
|
||||
mmu->nr_pages, mmu->nr_pmd_entries);
|
||||
|
||||
/* Allocate a page for the L1 table, always used for DomU */
|
||||
mmu->pmd_table.page = get_zeroed_page(GFP_KERNEL);
|
||||
if (!mmu->pmd_table.page)
|
||||
goto end;
|
||||
|
||||
mmu->pages_created++;
|
||||
|
||||
return mmu;
|
||||
|
||||
end:
|
||||
tee_mmu_delete(mmu);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static bool mmu_get_dma_buffer(struct tee_mmu *mmu, int va)
|
||||
{
|
||||
#ifdef CONFIG_DMA_SHARED_BUFFER
|
||||
struct dma_buf *buf;
|
||||
|
||||
buf = dma_buf_get(va);
|
||||
if (IS_ERR(buf))
|
||||
return false;
|
||||
|
||||
mmu->dma_buf = buf;
|
||||
mmu->attach = dma_buf_attach(mmu->dma_buf, g_ctx.mcd);
|
||||
if (IS_ERR(mmu->attach))
|
||||
goto err_attach;
|
||||
|
||||
mmu->sgt = dma_buf_map_attachment(mmu->attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(mmu->sgt))
|
||||
goto err_map;
|
||||
|
||||
return true;
|
||||
|
||||
err_map:
|
||||
dma_buf_detach(mmu->dma_buf, mmu->attach);
|
||||
|
||||
err_attach:
|
||||
dma_buf_put(mmu->dma_buf);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate MMU table and map buffer into it.
|
||||
* That is, create respective table entries.
|
||||
*/
|
||||
struct tee_mmu *tee_mmu_create(struct mm_struct *mm,
|
||||
const struct mc_ioctl_buffer *buf)
|
||||
{
|
||||
struct tee_mmu *mmu;
|
||||
const void *data = (const void *)(uintptr_t)buf->va;
|
||||
const void *reader = (const void *)((uintptr_t)data & PAGE_MASK);
|
||||
struct page **pages; /* Same as below, conveniently typed */
|
||||
unsigned long pages_page = 0; /* Page to contain the page pointers */
|
||||
unsigned long chunk;
|
||||
struct mcp_buffer_map b_map = {
|
||||
.offset = (u32)(buf->va & ~PAGE_MASK),
|
||||
.length = buf->len,
|
||||
.flags = buf->flags,
|
||||
};
|
||||
bool writeable = buf->flags & MC_IO_MAP_OUTPUT;
|
||||
int ret = 0;
|
||||
|
||||
#ifndef CONFIG_DMA_SHARED_BUFFER
|
||||
if (buf->flags & MMU_ION_BUF) {
|
||||
mc_dev_err(-EINVAL, "ION buffers not supported by kernel");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check input arguments */
|
||||
if (!(buf->flags & MMU_ION_BUF) && !buf->va)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (buf->flags & MMU_ION_BUF)
|
||||
/* buf->va is not a valid address. ION buffers are aligned */
|
||||
b_map.offset = 0;
|
||||
|
||||
/* Allocate the struct */
|
||||
b_map.nr_pages = PAGE_ALIGN(b_map.offset + b_map.length) / PAGE_SIZE;
|
||||
/* Allow Registered Shared mem with valid pointer and zero size. */
|
||||
if (!b_map.nr_pages)
|
||||
b_map.nr_pages = 1;
|
||||
|
||||
mmu = tee_mmu_create_common(&b_map);
|
||||
if (IS_ERR(mmu))
|
||||
return mmu;
|
||||
|
||||
if (buf->flags & MMU_ION_BUF) {
|
||||
mc_dev_devel("Buffer is ION");
|
||||
/* Buffer is ION -
|
||||
* va is the client's dma_buf fd, which should be converted
|
||||
* to a struct sg_table * directly.
|
||||
*/
|
||||
if (!mmu_get_dma_buffer(mmu, buf->va)) {
|
||||
mc_dev_err(ret, "mmu_get_dma_buffer failed");
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
/* Get a page to store page pointers */
|
||||
pages_page = get_zeroed_page(GFP_KERNEL);
|
||||
if (!pages_page) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
mmu->pages_created++;
|
||||
|
||||
pages = (struct page **)pages_page;
|
||||
for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) {
|
||||
unsigned long nr_pages;
|
||||
int i;
|
||||
|
||||
/* Size to map for this chunk */
|
||||
if (chunk == (mmu->nr_pmd_entries - 1))
|
||||
nr_pages = ((mmu->nr_pages - 1) % PTE_ENTRIES_MAX) + 1;
|
||||
else
|
||||
nr_pages = PTE_ENTRIES_MAX;
|
||||
|
||||
/* Allocate a page to hold ptes that describe buffer pages */
|
||||
mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL);
|
||||
if (!mmu->pte_tables[chunk].page) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
mmu->pages_created++;
|
||||
|
||||
/* Add page address to pmd table if needed */
|
||||
if (mmu->use_pages_and_vas)
|
||||
mmu->pmd_table.vas[chunk] =
|
||||
mmu->pte_tables[chunk].page;
|
||||
else
|
||||
mmu->pmd_table.entries[chunk] =
|
||||
virt_to_phys(mmu->pte_tables[chunk].addr);
|
||||
|
||||
/* Get pages */
|
||||
if (mmu->dma_buf) {
|
||||
/* Buffer is ION */
|
||||
struct sg_mapping_iter miter;
|
||||
struct page **page_ptr;
|
||||
unsigned int cnt = 0;
|
||||
unsigned int global_cnt = 0;
|
||||
|
||||
page_ptr = pages;
|
||||
sg_miter_start(&miter, mmu->sgt->sgl,
|
||||
mmu->sgt->nents,
|
||||
SG_MITER_FROM_SG);
|
||||
|
||||
while (sg_miter_next(&miter)) {
|
||||
if (((global_cnt) >=
|
||||
(PTE_ENTRIES_MAX * chunk)) &&
|
||||
cnt < nr_pages) {
|
||||
page_ptr[cnt] = miter.page;
|
||||
cnt++;
|
||||
}
|
||||
global_cnt++;
|
||||
}
|
||||
sg_miter_stop(&miter);
|
||||
} else if (mm) {
|
||||
long gup_ret;
|
||||
|
||||
/* Buffer was allocated in user space */
|
||||
#if KERNEL_VERSION(5, 7, 19) < LINUX_VERSION_CODE
|
||||
down_read(&mm->mmap_lock);
|
||||
#else
|
||||
down_read(&mm->mmap_sem);
|
||||
#endif
|
||||
/*
|
||||
* Always try to map read/write from a Linux PoV, so
|
||||
* Linux creates (page faults) the underlying pages if
|
||||
* missing.
|
||||
*/
|
||||
gup_ret = gup_local_repeat(mm, (uintptr_t)reader,
|
||||
nr_pages, 1, pages);
|
||||
if ((gup_ret == -EFAULT) && !writeable) {
|
||||
/*
|
||||
* If mapping read/write fails, and the buffer
|
||||
* is to be shared as input only, try to map
|
||||
* again read-only.
|
||||
*/
|
||||
gup_ret = gup_local_repeat(mm,
|
||||
(uintptr_t)reader,
|
||||
nr_pages, 0, pages);
|
||||
}
|
||||
#if KERNEL_VERSION(5, 7, 19) < LINUX_VERSION_CODE
|
||||
up_read(&mm->mmap_lock);
|
||||
#else
|
||||
up_read(&mm->mmap_sem);
|
||||
#endif
|
||||
if (gup_ret < 0) {
|
||||
ret = gup_ret;
|
||||
mc_dev_err(ret, "failed to get user pages @%p",
|
||||
reader);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* check if we could lock all pages. */
|
||||
if (gup_ret != nr_pages) {
|
||||
mc_dev_err((int)gup_ret,
|
||||
"failed to get user pages");
|
||||
#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
|
||||
release_pages(pages, gup_ret, 0);
|
||||
#else
|
||||
release_pages(pages, gup_ret);
|
||||
#endif
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
reader += nr_pages * PAGE_SIZE;
|
||||
mmu->user = true;
|
||||
mmu->pages_locked += nr_pages;
|
||||
} else if (is_vmalloc_addr(data)) {
|
||||
/* Buffer vmalloc'ed in kernel space */
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = vmalloc_to_page(reader);
|
||||
|
||||
if (!page) {
|
||||
ret = -EINVAL;
|
||||
mc_dev_err(ret,
|
||||
"failed to map address");
|
||||
goto end;
|
||||
}
|
||||
|
||||
pages[i] = page;
|
||||
reader += PAGE_SIZE;
|
||||
}
|
||||
} else {
|
||||
/* Buffer kmalloc'ed in kernel space */
|
||||
struct page *page = virt_to_page(reader);
|
||||
|
||||
reader += nr_pages * PAGE_SIZE;
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
pages[i] = page++;
|
||||
}
|
||||
|
||||
/* Create Table of physical addresses*/
|
||||
if (mmu->use_pages_and_vas) {
|
||||
memcpy(mmu->pte_tables[chunk].pages, pages,
|
||||
nr_pages * sizeof(*pages));
|
||||
} else {
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
mmu->pte_tables[chunk].entries[i] =
|
||||
page_to_phys(pages[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (pages_page) {
|
||||
free_page(pages_page);
|
||||
mmu->pages_created--;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
tee_mmu_delete(mmu);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
mc_dev_devel(
|
||||
"created mmu %p: %s va %llx len %u off %u flg %x pmd table %lx",
|
||||
mmu, mmu->user ? "user" : "kernel", buf->va, mmu->length,
|
||||
mmu->offset, mmu->flags, mmu->pmd_table.page);
|
||||
return mmu;
|
||||
}
|
||||
|
||||
struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages,
|
||||
const struct mcp_buffer_map *b_map)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
#ifdef CONFIG_XEN
|
||||
struct tee_mmu *mmu;
|
||||
unsigned long chunk, nr_pages_left;
|
||||
|
||||
/* Allocate the struct */
|
||||
mmu = tee_mmu_create_common(b_map);
|
||||
if (IS_ERR(mmu))
|
||||
return mmu;
|
||||
|
||||
nr_pages_left = mmu->nr_pages;
|
||||
for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) {
|
||||
unsigned long nr_pages = nr_pages_left;
|
||||
u64 *pte;
|
||||
int i;
|
||||
|
||||
if (nr_pages > PTE_ENTRIES_MAX)
|
||||
nr_pages = PTE_ENTRIES_MAX;
|
||||
|
||||
nr_pages_left -= nr_pages;
|
||||
|
||||
/* Allocate a page to hold ptes that describe buffer pages */
|
||||
mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL);
|
||||
if (!mmu->pte_tables[chunk].page) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
mmu->pages_created++;
|
||||
|
||||
/* Add page address to pmd table if needed */
|
||||
mmu->pmd_table.entries[chunk] =
|
||||
virt_to_phys(mmu->pte_tables[chunk].addr);
|
||||
|
||||
/* Convert to PTEs */
|
||||
pte = &mmu->pte_tables[chunk].entries[0];
|
||||
|
||||
for (i = 0; i < nr_pages; i++, pages++, pte++) {
|
||||
unsigned long phys;
|
||||
unsigned long pfn;
|
||||
|
||||
phys = page_to_phys(*pages);
|
||||
#if defined CONFIG_ARM64
|
||||
phys &= PHYS_48BIT_MASK;
|
||||
#endif
|
||||
pfn = PFN_DOWN(phys);
|
||||
*pte = __pfn_to_mfn(pfn) << PAGE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
mmu->deleter = deleter;
|
||||
mc_dev_devel("wrapped mmu %p: len %u off %u flg %x pmd table %lx",
|
||||
mmu, mmu->length, mmu->offset, mmu->flags,
|
||||
mmu->pmd_table.page);
|
||||
return mmu;
|
||||
|
||||
err:
|
||||
tee_mmu_delete(mmu);
|
||||
#endif
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter)
|
||||
{
|
||||
mmu->deleter = deleter;
|
||||
}
|
||||
|
||||
static void tee_mmu_release(struct kref *kref)
|
||||
{
|
||||
struct tee_mmu *mmu = container_of(kref, struct tee_mmu, kref);
|
||||
|
||||
mc_dev_devel("free mmu %p: %s len %u off %u pmd table %lx",
|
||||
mmu, mmu->user ? "user" : "kernel", mmu->length,
|
||||
mmu->offset, mmu->pmd_table.page);
|
||||
tee_mmu_delete(mmu);
|
||||
}
|
||||
|
||||
void tee_mmu_get(struct tee_mmu *mmu)
|
||||
{
|
||||
kref_get(&mmu->kref);
|
||||
}
|
||||
|
||||
void tee_mmu_put(struct tee_mmu *mmu)
|
||||
{
|
||||
kref_put(&mmu->kref, tee_mmu_release);
|
||||
}
|
||||
|
||||
void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map)
|
||||
{
|
||||
if (mmu->use_pages_and_vas)
|
||||
map->addr = mmu->pmd_table.page;
|
||||
else
|
||||
map->addr = virt_to_phys(mmu->pmd_table.addr);
|
||||
|
||||
map->secure_va = 0;
|
||||
map->offset = mmu->offset;
|
||||
map->length = mmu->length;
|
||||
map->nr_pages = mmu->nr_pages;
|
||||
map->flags = mmu->flags;
|
||||
map->type = WSM_L1;
|
||||
map->mmu = mmu;
|
||||
}
|
||||
|
||||
int tee_mmu_debug_structs(struct kasnprintf_buf *buf, const struct tee_mmu *mmu)
|
||||
{
|
||||
return kasnprintf(buf,
|
||||
"\t\t\tmmu %pK: %s len %u off %u table %pK\n",
|
||||
mmu, mmu->user ? "user" : "kernel", mmu->length,
|
||||
mmu->offset, (void *)mmu->pmd_table.page);
|
||||
}
|
||||
82
drivers/gud/MobiCoreDriver/mmu.h
Normal file
82
drivers/gud/MobiCoreDriver/mmu.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TBASE_MEM_H_
|
||||
#define _TBASE_MEM_H_
|
||||
|
||||
/*
|
||||
* This represents the maximum number of entries in a Page Table Entries
|
||||
* array which maps one 4KiB page. Each entry is 64 bits long physical
|
||||
* address with some possible flags. With 512 entries it is possible
|
||||
* to map 2MiB memory block.
|
||||
*/
|
||||
#define PTE_ENTRIES_MAX 512
|
||||
|
||||
/*
|
||||
* This represents the maximum number of entries in a Page Middle Directory
|
||||
* which maps one 4KiB page. Each entry is a 64 bits physical address that
|
||||
* points to a PTE. With 512 entries t is possible to map 1GB memory block.
|
||||
*/
|
||||
#define PMD_ENTRIES_MAX 512
|
||||
|
||||
struct tee_mmu;
|
||||
struct mcp_buffer_map;
|
||||
|
||||
struct tee_deleter {
|
||||
void *object;
|
||||
void (*delete)(void *object);
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocate MMU table and map buffer into it.
|
||||
* That is, create respective table entries.
|
||||
*/
|
||||
struct tee_mmu *tee_mmu_create(struct mm_struct *mm,
|
||||
const struct mc_ioctl_buffer *buf);
|
||||
|
||||
/*
|
||||
* Allocate MMU table and map pages into it.
|
||||
* This is for Xen Dom0 to re-create a buffer with existing pages.
|
||||
*/
|
||||
struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages,
|
||||
const struct mcp_buffer_map *buf);
|
||||
|
||||
/*
|
||||
* Give the MMU an object to release when released
|
||||
*/
|
||||
void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter);
|
||||
|
||||
/*
|
||||
* Gets a reference on a MMU table.
|
||||
*/
|
||||
void tee_mmu_get(struct tee_mmu *mmu);
|
||||
|
||||
/*
|
||||
* Puts a reference on a MMU table.
|
||||
*/
|
||||
void tee_mmu_put(struct tee_mmu *mmu);
|
||||
|
||||
/*
|
||||
* Fill in buffer info for MMU table.
|
||||
*/
|
||||
void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map);
|
||||
|
||||
/*
|
||||
* Add info to debug buffer.
|
||||
*/
|
||||
int tee_mmu_debug_structs(struct kasnprintf_buf *buf,
|
||||
const struct tee_mmu *mmu);
|
||||
|
||||
#endif /* _TBASE_MEM_H_ */
|
||||
467
drivers/gud/MobiCoreDriver/mobicore_driver_api.h
Normal file
467
drivers/gud/MobiCoreDriver/mobicore_driver_api.h
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _MOBICORE_DRIVER_API_H_
|
||||
#define _MOBICORE_DRIVER_API_H_
|
||||
|
||||
#include "mc_user.h"
|
||||
|
||||
#define __MC_CLIENT_LIB_API
|
||||
|
||||
/*
|
||||
* Return values of MobiCore driver functions.
|
||||
*/
|
||||
enum mc_result {
|
||||
/* Function call succeeded. */
|
||||
MC_DRV_OK = 0,
|
||||
/* No notification available. */
|
||||
MC_DRV_NO_NOTIFICATION = 1,
|
||||
/* Error during notification on communication level. */
|
||||
MC_DRV_ERR_NOTIFICATION = 2,
|
||||
/* Function not implemented. */
|
||||
MC_DRV_ERR_NOT_IMPLEMENTED = 3,
|
||||
/* No more resources available. */
|
||||
MC_DRV_ERR_OUT_OF_RESOURCES = 4,
|
||||
/* Driver initialization failed. */
|
||||
MC_DRV_ERR_INIT = 5,
|
||||
/* Unknown error. */
|
||||
MC_DRV_ERR_UNKNOWN = 6,
|
||||
/* The specified device is unknown. */
|
||||
MC_DRV_ERR_UNKNOWN_DEVICE = 7,
|
||||
/* The specified session is unknown.*/
|
||||
MC_DRV_ERR_UNKNOWN_SESSION = 8,
|
||||
/* The specified operation is not allowed. */
|
||||
MC_DRV_ERR_INVALID_OPERATION = 9,
|
||||
/* The response header from the MC is invalid. */
|
||||
MC_DRV_ERR_INVALID_RESPONSE = 10,
|
||||
/* Function call timed out. */
|
||||
MC_DRV_ERR_TIMEOUT = 11,
|
||||
/* Can not allocate additional memory. */
|
||||
MC_DRV_ERR_NO_FREE_MEMORY = 12,
|
||||
/* Free memory failed. */
|
||||
MC_DRV_ERR_FREE_MEMORY_FAILED = 13,
|
||||
/* Still some open sessions pending. */
|
||||
MC_DRV_ERR_SESSION_PENDING = 14,
|
||||
/* MC daemon not reachable */
|
||||
MC_DRV_ERR_DAEMON_UNREACHABLE = 15,
|
||||
/* The device file of the kernel module could not be opened. */
|
||||
MC_DRV_ERR_INVALID_DEVICE_FILE = 16,
|
||||
/* Invalid parameter. */
|
||||
MC_DRV_ERR_INVALID_PARAMETER = 17,
|
||||
/* Unspecified error from Kernel Module*/
|
||||
MC_DRV_ERR_KERNEL_MODULE = 18,
|
||||
/* Error during mapping of additional bulk memory to session. */
|
||||
MC_DRV_ERR_BULK_MAPPING = 19,
|
||||
/* Error during unmapping of additional bulk memory to session. */
|
||||
MC_DRV_ERR_BULK_UNMAPPING = 20,
|
||||
/* Notification received, exit code available. */
|
||||
MC_DRV_INFO_NOTIFICATION = 21,
|
||||
/* Set up of NWd connection failed. */
|
||||
MC_DRV_ERR_NQ_FAILED = 22,
|
||||
/* Wrong daemon version. */
|
||||
MC_DRV_ERR_DAEMON_VERSION = 23,
|
||||
/* Wrong container version. */
|
||||
MC_DRV_ERR_CONTAINER_VERSION = 24,
|
||||
/* System Trustlet public key is wrong. */
|
||||
MC_DRV_ERR_WRONG_PUBLIC_KEY = 25,
|
||||
/* Wrong container type(s). */
|
||||
MC_DRV_ERR_CONTAINER_TYPE_MISMATCH = 26,
|
||||
/* Container is locked (or not activated). */
|
||||
MC_DRV_ERR_CONTAINER_LOCKED = 27,
|
||||
/* SPID is not registered with root container. */
|
||||
MC_DRV_ERR_SP_NO_CHILD = 28,
|
||||
/* UUID is not registered with sp container. */
|
||||
MC_DRV_ERR_TL_NO_CHILD = 29,
|
||||
/* Unwrapping of root container failed. */
|
||||
MC_DRV_ERR_UNWRAP_ROOT_FAILED = 30,
|
||||
/* Unwrapping of service provider container failed. */
|
||||
MC_DRV_ERR_UNWRAP_SP_FAILED = 31,
|
||||
/* Unwrapping of Trustlet container failed. */
|
||||
MC_DRV_ERR_UNWRAP_TRUSTLET_FAILED = 32,
|
||||
/* No device associated with connection. */
|
||||
MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN = 33,
|
||||
/* TA blob attestation is incorrect. */
|
||||
MC_DRV_ERR_TA_ATTESTATION_ERROR = 34,
|
||||
/* Interrupted system call. */
|
||||
MC_DRV_ERR_INTERRUPTED_BY_SIGNAL = 35,
|
||||
/* Service is blocked and opensession is thus not allowed. */
|
||||
MC_DRV_ERR_SERVICE_BLOCKED = 36,
|
||||
/* Service is locked and opensession is thus not allowed. */
|
||||
MC_DRV_ERR_SERVICE_LOCKED = 37,
|
||||
/* Service was killed by the TEE (due to an administrative command). */
|
||||
MC_DRV_ERR_SERVICE_KILLED = 38,
|
||||
/* All permitted instances to the service are used */
|
||||
MC_DRV_ERR_NO_FREE_INSTANCES = 39,
|
||||
/* TA blob header is incorrect. */
|
||||
MC_DRV_ERR_TA_HEADER_ERROR = 40,
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure of Session Handle, includes the Session ID and the Device ID the
|
||||
* Session belongs to.
|
||||
* The session handle will be used for session-based MobiCore communication.
|
||||
* It will be passed to calls which address a communication end point in the
|
||||
* MobiCore environment.
|
||||
*/
|
||||
struct mc_session_handle {
|
||||
u32 session_id; /* MobiCore session ID */
|
||||
u32 device_id; /* Device ID the session belongs to */
|
||||
};
|
||||
|
||||
/*
|
||||
* Information structure about additional mapped Bulk buffer between the
|
||||
* Trustlet Connector (NWd) and the Trustlet (SWd). This structure is
|
||||
* initialized from a Trustlet Connector by calling mc_map().
|
||||
* In order to use the memory within a Trustlet the Trustlet Connector has to
|
||||
* inform the Trustlet with the content of this structure via the TCI.
|
||||
*/
|
||||
struct mc_bulk_map {
|
||||
/*
|
||||
* The virtual address of the Bulk buffer regarding the address space
|
||||
* of the Trustlet, already includes a possible offset!
|
||||
*/
|
||||
u32 secure_virt_addr;
|
||||
u32 secure_virt_len; /* Length of the mapped Bulk buffer */
|
||||
};
|
||||
|
||||
/* The default device ID */
|
||||
#define MC_DEVICE_ID_DEFAULT 0
|
||||
/* Wait infinite for a response of the MC. */
|
||||
#define MC_INFINITE_TIMEOUT ((s32)(-1))
|
||||
/* Do not wait for a response of the MC. */
|
||||
#define MC_NO_TIMEOUT 0
|
||||
/* TCI/DCI must not exceed 1MiB */
|
||||
#define MC_MAX_TCI_LEN 0x100000
|
||||
|
||||
/**
|
||||
* mc_open_device() - Open a new connection to a MobiCore device.
|
||||
* @device_id: Identifier for the MobiCore device to be used.
|
||||
* MC_DEVICE_ID_DEFAULT refers to the default device.
|
||||
*
|
||||
* Initializes all device specific resources required to communicate with a
|
||||
* MobiCore instance located on the specified device in the system. If the
|
||||
* device does not exist the function will return MC_DRV_ERR_UNKNOWN_DEVICE.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_ERR_INVALID_OPERATION: device already opened
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device_id unknown
|
||||
* MC_DRV_ERR_INVALID_DEVICE_FILE: kernel module under /dev/mobicore
|
||||
* cannot be opened
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_open_device(
|
||||
u32 device_id);
|
||||
|
||||
/**
|
||||
* mc_close_device() - Close the connection to a MobiCore device.
|
||||
* @device_id: Identifier for the MobiCore device.
|
||||
*
|
||||
* When closing a device, active sessions have to be closed beforehand.
|
||||
* Resources associated with the device will be released.
|
||||
* The device may be opened again after it has been closed.
|
||||
*
|
||||
* MC_DEVICE_ID_DEFAULT refers to the default device.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
|
||||
* MC_DRV_ERR_SESSION_PENDING: a session is still open
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_close_device(
|
||||
u32 device_id);
|
||||
|
||||
/**
|
||||
* mc_open_session() - Open a new session to a Trustlet.
|
||||
* @session: On success, the session data will be returned
|
||||
* @uuid: UUID of the Trustlet to be opened
|
||||
* @tci: TCI buffer for communicating with the Trustlet
|
||||
* @tci_len: Length of the TCI buffer. Maximum allowed value
|
||||
* is MC_MAX_TCI_LEN
|
||||
*
|
||||
* The Trustlet with the given UUID has to be available in the flash filesystem.
|
||||
*
|
||||
* Write MCP open message to buffer and notify MobiCore about the availability
|
||||
* of a new command.
|
||||
*
|
||||
* Waits till the MobiCore responses with the new session ID (stored in the MCP
|
||||
* buffer).
|
||||
*
|
||||
* Note that session.device_id has to be the device id of an opened device.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: session parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon socket occur
|
||||
* MC_DRV_ERR_NQ_FAILED: daemon returns an error
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_open_session(
|
||||
struct mc_session_handle *session,
|
||||
const struct mc_uuid_t *uuid,
|
||||
u8 *tci,
|
||||
u32 tci_len);
|
||||
|
||||
/**
|
||||
* mc_open_trustlet() - Open a new session to the provided Trustlet.
|
||||
* @session: On success, the session data will be returned
|
||||
* @spid: Service Provider ID (for SP trustlets otherwise ignored)
|
||||
* @trustlet Memory buffer containing the Trusted Application binary
|
||||
* @trustlet_len Trusted Application length
|
||||
* @tci: TCI buffer for communicating with the Trustlet
|
||||
* @tci_len: Length of the TCI buffer. Maximum allowed value
|
||||
* is MC_MAX_TCI_LEN
|
||||
*
|
||||
* Write MCP open message to buffer and notify MobiCore about the availability
|
||||
* of a new command.
|
||||
*
|
||||
* Waits till the MobiCore responses with the new session ID (stored in the MCP
|
||||
* buffer).
|
||||
*
|
||||
* Note that session.device_id has to be the device id of an opened device.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: session parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon socket occur
|
||||
* MC_DRV_ERR_NQ_FAILED: daemon returns an error
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_open_trustlet(
|
||||
struct mc_session_handle *session,
|
||||
u32 spid,
|
||||
u8 *trustlet,
|
||||
u32 trustlet_len,
|
||||
u8 *tci,
|
||||
u32 len);
|
||||
|
||||
/**
|
||||
* mc_close_session() - Close a Trustlet session.
|
||||
* @session: Session to be closed.
|
||||
*
|
||||
* Closes the specified MobiCore session. The call will block until the
|
||||
* session has been closed.
|
||||
*
|
||||
* Device device_id has to be opened in advance.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: session parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
|
||||
* MC_DRV_ERR_INVALID_DEVICE_FILE: daemon cannot open Trustlet file
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_close_session(
|
||||
struct mc_session_handle *session);
|
||||
|
||||
/**
|
||||
* mc_notify() - Notify a session.
|
||||
* @session: The session to be notified.
|
||||
*
|
||||
* Notifies the session end point about available message data.
|
||||
* If the session parameter is correct, notify will always succeed.
|
||||
* Corresponding errors can only be received by mc_wait_notification().
|
||||
*
|
||||
* A session has to be opened in advance.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: session parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_notify(
|
||||
struct mc_session_handle *session);
|
||||
|
||||
/**
|
||||
* mc_wait_notification() - Wait for a notification.
|
||||
* @session: The session the notification should correspond to.
|
||||
* @timeout: Time in milliseconds to wait
|
||||
* (MC_NO_TIMEOUT : direct return, > 0 : milliseconds,
|
||||
* MC_INFINITE_TIMEOUT : wait infinitely)
|
||||
*
|
||||
* Wait for a notification issued by the MobiCore for a specific session.
|
||||
* The timeout parameter specifies the number of milliseconds the call will wait
|
||||
* for a notification.
|
||||
*
|
||||
* If the caller passes 0 as timeout value the call will immediately return.
|
||||
* If timeout value is below 0 the call will block until a notification for the
|
||||
* session has been received.
|
||||
*
|
||||
* If timeout is below 0, call will block.
|
||||
*
|
||||
* Caller has to trust the other side to send a notification to wake him up
|
||||
* again.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_ERR_TIMEOUT: no notification arrived in time
|
||||
* MC_DRV_INFO_NOTIFICATION: a problem with the session was
|
||||
* encountered. Get more details with
|
||||
* mc_get_session_error_code()
|
||||
* MC_DRV_ERR_NOTIFICATION: a problem with the socket occurred
|
||||
* MC_DRV_INVALID_PARAMETER: a parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_wait_notification(
|
||||
struct mc_session_handle *session,
|
||||
s32 timeout);
|
||||
|
||||
/**
|
||||
* mc_malloc_wsm() - Allocate a block of world shared memory (WSM).
|
||||
* @device_id: The ID of an opened device to retrieve the WSM from.
|
||||
* @align: The alignment (number of pages) of the memory block
|
||||
* (e.g. 0x00000001 for 4kb).
|
||||
* @len: Length of the block in bytes.
|
||||
* @wsm: Virtual address of the world shared memory block.
|
||||
* @wsm_flags: Platform specific flags describing the memory to
|
||||
* be allocated.
|
||||
*
|
||||
* The MC driver allocates a contiguous block of memory which can be used as
|
||||
* WSM.
|
||||
* This implicates that the allocated memory is aligned according to the
|
||||
* alignment parameter.
|
||||
*
|
||||
* Always returns a buffer of size WSM_SIZE aligned to 4K.
|
||||
*
|
||||
* Align and wsm_flags are currently ignored
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: a parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
|
||||
* MC_DRV_ERR_NO_FREE_MEMORY: no more contiguous memory is
|
||||
* available in this size or for this
|
||||
* process
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_malloc_wsm(
|
||||
u32 device_id,
|
||||
u32 align,
|
||||
u32 len,
|
||||
u8 **wsm,
|
||||
u32 wsm_flags);
|
||||
|
||||
/**
|
||||
* mc_free_wsm() - Free a block of world shared memory (WSM).
|
||||
* @device_id: The ID to which the given address belongs
|
||||
* @wsm: Address of WSM block to be freed
|
||||
*
|
||||
* The MC driver will free a block of world shared memory (WSM) previously
|
||||
* allocated with mc_malloc_wsm(). The caller has to assure that the address
|
||||
* handed over to the driver is a valid WSM address.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: a parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: when device id is invalid
|
||||
* MC_DRV_ERR_FREE_MEMORY_FAILED: on failure
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_free_wsm(
|
||||
u32 device_id,
|
||||
u8 *wsm);
|
||||
|
||||
/**
|
||||
*mc_map() - Map additional bulk buffer between a Trustlet Connector (TLC)
|
||||
* and the Trustlet (TL) for a session
|
||||
* @session: Session handle with information of the device_id and
|
||||
* the session_id. The given buffer is mapped to the
|
||||
* session specified in the sessionHandle
|
||||
* @buf: Virtual address of a memory portion (relative to TLC)
|
||||
* to be shared with the Trustlet, already includes a
|
||||
* possible offset!
|
||||
* @len: length of buffer block in bytes.
|
||||
* @map_info: Information structure about the mapped Bulk buffer
|
||||
* between the TLC (NWd) and the TL (SWd).
|
||||
*
|
||||
* Memory allocated in user space of the TLC can be mapped as additional
|
||||
* communication channel (besides TCI) to the Trustlet. Limitation of the
|
||||
* Trustlet memory structure apply: only 6 chunks can be mapped with a maximum
|
||||
* chunk size of 1 MiB each.
|
||||
*
|
||||
* It is up to the application layer (TLC) to inform the Trustlet
|
||||
* about the additional mapped bulk memory.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: a parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
|
||||
* MC_DRV_ERR_BULK_MAPPING: buf is already uses as bulk buffer or
|
||||
* when registering the buffer failed
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_map(
|
||||
struct mc_session_handle *session,
|
||||
void *buf,
|
||||
u32 len,
|
||||
struct mc_bulk_map *map_info);
|
||||
|
||||
/**
|
||||
* mc_unmap() - Remove additional mapped bulk buffer between Trustlet Connector
|
||||
* (TLC) and the Trustlet (TL) for a session
|
||||
* @session: Session handle with information of the device_id and
|
||||
* the session_id. The given buffer is unmapped from the
|
||||
* session specified in the sessionHandle.
|
||||
* @buf: Virtual address of a memory portion (relative to TLC)
|
||||
* shared with the TL, already includes a possible offset!
|
||||
* @map_info: Information structure about the mapped Bulk buffer
|
||||
* between the TLC (NWd) and the TL (SWd)
|
||||
*
|
||||
* The bulk buffer will immediately be unmapped from the session context.
|
||||
*
|
||||
* The application layer (TLC) must inform the TL about unmapping of the
|
||||
* additional bulk memory before calling mc_unmap!
|
||||
*
|
||||
* The clientlib currently ignores the len field in map_info.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: a parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
|
||||
* MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
|
||||
* MC_DRV_ERR_BULK_UNMAPPING: buf was not registered earlier
|
||||
* or when unregistering failed
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_unmap(
|
||||
struct mc_session_handle *session,
|
||||
void *buf,
|
||||
struct mc_bulk_map *map_info);
|
||||
|
||||
/*
|
||||
* mc_get_session_error_code() - Get additional error information of the last
|
||||
* error that occurred on a session.
|
||||
* @session: Session handle with information of the device_id and
|
||||
* the session_id
|
||||
* @exit_code: >0 Trustlet has terminated itself with this value,
|
||||
* <0 Trustlet is dead because of an error within the
|
||||
* MobiCore (e.g. Kernel exception). See also MCI
|
||||
* definition.
|
||||
*
|
||||
* After the request the stored error code will be deleted.
|
||||
*
|
||||
* Return codes:
|
||||
* MC_DRV_OK: operation completed successfully
|
||||
* MC_DRV_INVALID_PARAMETER: a parameter is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
|
||||
* MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
|
||||
*/
|
||||
__MC_CLIENT_LIB_API enum mc_result mc_get_session_error_code(
|
||||
struct mc_session_handle *session,
|
||||
s32 *exit_code);
|
||||
|
||||
#endif /* _MOBICORE_DRIVER_API_H_ */
|
||||
1179
drivers/gud/MobiCoreDriver/nq.c
Normal file
1179
drivers/gud/MobiCoreDriver/nq.c
Normal file
File diff suppressed because it is too large
Load diff
87
drivers/gud/MobiCoreDriver/nq.h
Normal file
87
drivers/gud/MobiCoreDriver/nq.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_NQ_H_
|
||||
#define _MC_NQ_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
/** Max number of interworld session allocated in MCI buffer */
|
||||
#define MAX_IW_SESSION 256
|
||||
|
||||
enum nq_notif_state {
|
||||
NQ_NOTIF_IDLE, /* Nothing happened yet */
|
||||
NQ_NOTIF_QUEUED, /* Notification in overflow queue */
|
||||
NQ_NOTIF_SENT, /* Notification in send queue */
|
||||
NQ_NOTIF_RECEIVED, /* Notification received */
|
||||
NQ_NOTIF_CONSUMED, /* Notification reported to CA */
|
||||
NQ_NOTIF_DEAD, /* Error reported to CA */
|
||||
};
|
||||
|
||||
/* FIXME to be renamed */
|
||||
struct nq_session {
|
||||
/* Notification id */
|
||||
u32 id;
|
||||
/* Notification payload */
|
||||
u32 payload;
|
||||
/* Notifications list */
|
||||
struct list_head list;
|
||||
/* Notification debug mutex */
|
||||
struct mutex mutex;
|
||||
/* Current notification/session state */
|
||||
enum nq_notif_state state;
|
||||
/* Time at notification state change */
|
||||
u64 cpu_clk;
|
||||
/* This TA is of Global Platform type, set by upper layer */
|
||||
int is_gp;
|
||||
};
|
||||
|
||||
/* Notification queue channel */
|
||||
void nq_session_init(struct nq_session *session, bool is_gp);
|
||||
void nq_session_exit(struct nq_session *session);
|
||||
void nq_session_state_update(struct nq_session *session,
|
||||
enum nq_notif_state state);
|
||||
int nq_session_notify(struct nq_session *session, u32 id, u32 payload);
|
||||
const char *nq_session_state(const struct nq_session *session, u64 *cpu_clk);
|
||||
|
||||
/* Services */
|
||||
union mcp_message *nq_get_mcp_buffer(void);
|
||||
struct interworld_session *nq_get_iwp_buffer(void);
|
||||
void nq_set_version_ptr(char *version);
|
||||
void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp);
|
||||
int nq_register_tee_stop_notifier(struct notifier_block *nb);
|
||||
int nq_unregister_tee_stop_notifier(struct notifier_block *nb);
|
||||
ssize_t nq_get_stop_message(char __user *buffer, size_t size);
|
||||
void nq_signal_tee_hung(void);
|
||||
|
||||
/* SWd suspend/resume */
|
||||
void add_core_to_mask(unsigned int cpu);
|
||||
void remove_core_from_mask(unsigned int cpu);
|
||||
int nq_cpu_on(unsigned int cpu);
|
||||
int nq_cpu_off(unsigned int cpu);
|
||||
int nq_suspend(void);
|
||||
int nq_resume(void);
|
||||
|
||||
/* Start/stop TEE */
|
||||
int nq_start(void);
|
||||
void nq_stop(void);
|
||||
|
||||
/* Initialisation/cleanup */
|
||||
int nq_init(void);
|
||||
void nq_exit(void);
|
||||
|
||||
#endif /* _MC_NQ_H_ */
|
||||
178
drivers/gud/MobiCoreDriver/platform.h
Normal file
178
drivers/gud/MobiCoreDriver/platform.h
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _MC_PLATFORM_H_
|
||||
#define _MC_PLATFORM_H_
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
|
||||
#define USE_SHM_BRIDGE
|
||||
#endif
|
||||
|
||||
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <soc/qcom/qseecomi.h>
|
||||
#if defined USE_SHM_BRIDGE
|
||||
#include <linux/qtee_shmbridge.h>
|
||||
#endif
|
||||
#elif KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE
|
||||
#include <soc/qcom/scm.h>
|
||||
#include <soc/qcom/qseecomi.h>
|
||||
#if defined USE_SHM_BRIDGE
|
||||
#include <soc/qcom/qtee_shmbridge.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*--------------- Implementation -------------- */
|
||||
/* MobiCore Interrupt for Qualcomm (DT IRQ has priority if present) */
|
||||
#define MC_INTR_SSIQ 280
|
||||
|
||||
/* Use SMC for fastcalls */
|
||||
#define MC_SMC_FASTCALL
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0)
|
||||
#define SCM_MOBIOS_FNID(s, c) (((((s) & 0xFF) << 8) | ((c) & 0xFF)) \
|
||||
| 0x33000000)
|
||||
|
||||
#define TZ_EXECUTIVE_EXT_ID_PARAM_ID \
|
||||
TZ_SYSCALL_CREATE_PARAM_ID_4( \
|
||||
TZ_SYSCALL_PARAM_TYPE_BUF_RW, \
|
||||
TZ_SYSCALL_PARAM_TYPE_VAL, \
|
||||
TZ_SYSCALL_PARAM_TYPE_BUF_RW, \
|
||||
TZ_SYSCALL_PARAM_TYPE_VAL)
|
||||
#endif
|
||||
|
||||
/* from following file */
|
||||
#define SCM_SVC_MOBICORE 250
|
||||
#define SCM_CMD_MOBICORE 1
|
||||
|
||||
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
|
||||
extern int trustonic_smc_fastcall(void *fc_generic, size_t size);
|
||||
static inline int smc_fastcall(void *fc_generic, size_t size)
|
||||
{
|
||||
return trustonic_smc_fastcall(fc_generic, size);
|
||||
}
|
||||
|
||||
#elif KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE
|
||||
static inline int smc_fastcall(void *fc_generic, size_t size)
|
||||
{
|
||||
#if !defined(USE_SHM_BRIDGE)
|
||||
if (is_scm_armv8()) {
|
||||
#endif
|
||||
struct scm_desc desc = {0};
|
||||
int ret;
|
||||
static void *scm_buf;
|
||||
static phys_addr_t scm_buf_pa;
|
||||
|
||||
if (!scm_buf) {
|
||||
#if defined USE_SHM_BRIDGE
|
||||
static struct qtee_shm scm_shm = {0};
|
||||
|
||||
ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(size),
|
||||
&scm_shm);
|
||||
scm_buf = scm_shm.vaddr;
|
||||
scm_buf_pa = scm_shm.paddr;
|
||||
#else
|
||||
scm_buf = kzalloc(PAGE_ALIGN(size), GFP_KERNEL);
|
||||
scm_buf_pa = virt_to_phys(scm_buf);
|
||||
#endif
|
||||
}
|
||||
if (!scm_buf)
|
||||
return -ENOMEM;
|
||||
memcpy(scm_buf, fc_generic, size);
|
||||
dmac_flush_range(scm_buf, scm_buf + size);
|
||||
|
||||
desc.arginfo = TZ_EXECUTIVE_EXT_ID_PARAM_ID;
|
||||
desc.args[0] = scm_buf_pa;
|
||||
desc.args[1] = (u32)size;
|
||||
desc.args[2] = scm_buf_pa;
|
||||
desc.args[3] = (u32)size;
|
||||
|
||||
ret = scm_call2(
|
||||
SCM_MOBIOS_FNID(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE),
|
||||
&desc);
|
||||
|
||||
dmac_flush_range(scm_buf, scm_buf + size);
|
||||
|
||||
memcpy(fc_generic, scm_buf, size);
|
||||
return ret;
|
||||
#if !defined(USE_SHM_BRIDGE)
|
||||
}
|
||||
return scm_call(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE,
|
||||
fc_generic, size,
|
||||
fc_generic, size);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do not start the TEE at driver init
|
||||
*/
|
||||
#define MC_DELAYED_TEE_START
|
||||
|
||||
/*
|
||||
* Perform crypto clock enable/disable
|
||||
* of clocks
|
||||
* "bus_clk"
|
||||
* "core_clk"
|
||||
* "iface_clk"
|
||||
*/
|
||||
#ifndef RSU_INTERNAL_CLOCK
|
||||
#define MC_CRYPTO_CLOCK_MANAGEMENT
|
||||
#endif
|
||||
/*
|
||||
* This should be defined only on platforms without ICE clock e.g SDM845, SM6150, SM7225
|
||||
* #define TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE "qcom,no-clock-support"
|
||||
*/
|
||||
#define MC_CRYPTO_CLOCK_CORESRC_PROPNAME "qcom,ce-opp-freq"
|
||||
|
||||
#if defined USE_SHM_BRIDGE
|
||||
#define MC_CLOCK_CORESRC_DEFAULTRATE 192000000
|
||||
#else
|
||||
#define MC_CLOCK_CORESRC_DEFAULTRATE 100000000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Perform clock enable/disable for clock "core_clk_src"
|
||||
*/
|
||||
#define MC_DEVICE_PROPNAME "qcom,mcd"
|
||||
/*
|
||||
* Platform Node
|
||||
*/
|
||||
#define TT_CLOCK_DEVICE_NAME "qcom,qseecom"
|
||||
|
||||
/* All TZBSPv4 targets are using AARCH32_FC flag */
|
||||
#define MC_AARCH32_FC
|
||||
|
||||
/* This should be defined only on some platforms e.g SM8150, SM6150, SM7225
|
||||
* from which the Gold cores do not support TEE interfaces
|
||||
* so that CPU_IDS should list only Silver cores.
|
||||
*/
|
||||
/* Enforce/restrict statically CPUs potentially running TEE
|
||||
* (Customize to match platform CPU layout... 0xF0 for big cores only for ex).
|
||||
* If not defined TEE dynamically using all platform CPUs (recommended)
|
||||
* Warning: Both PLAT_DEFAULT_TEE_AFFINITY_MASK and
|
||||
* BIG_CORE_SWITCH_AFFINITY_MASK have to be defined
|
||||
*/
|
||||
#define PLAT_DEFAULT_TEE_AFFINITY_MASK (0xF)
|
||||
#define BIG_CORE_SWITCH_AFFINITY_MASK (0xF)
|
||||
|
||||
#endif /* _MC_PLATFORM_H_ */
|
||||
1013
drivers/gud/MobiCoreDriver/session.c
Normal file
1013
drivers/gud/MobiCoreDriver/session.c
Normal file
File diff suppressed because it is too large
Load diff
114
drivers/gud/MobiCoreDriver/session.h
Normal file
114
drivers/gud/MobiCoreDriver/session.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SESSION_H_
|
||||
#define _SESSION_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "mcp.h"
|
||||
#include "iwp.h"
|
||||
|
||||
struct tee_object;
|
||||
struct tee_mmu;
|
||||
struct mc_ioctl_buffer;
|
||||
|
||||
struct tee_wsm {
|
||||
/* Buffer NWd address (uva or kva, used only for lookup) */
|
||||
uintptr_t va;
|
||||
/* Buffer length */
|
||||
u32 len;
|
||||
/* Buffer flags */
|
||||
u32 flags;
|
||||
/* Buffer SWd address */
|
||||
u32 sva;
|
||||
union {
|
||||
/* MMU table */
|
||||
struct tee_mmu *mmu;
|
||||
/* Index of re-used buffer (temporary) */
|
||||
int index;
|
||||
};
|
||||
/* Pointer to associated cbuf, if relevant */
|
||||
struct cbuf *cbuf;
|
||||
/* State of this WSM */
|
||||
int in_use;
|
||||
};
|
||||
|
||||
struct tee_session {
|
||||
/* Session descriptor */
|
||||
union {
|
||||
struct mcp_session mcp_session;
|
||||
struct iwp_session iwp_session;
|
||||
};
|
||||
/* Owner */
|
||||
struct tee_client *client;
|
||||
/* Number of references kept to this object */
|
||||
struct kref kref;
|
||||
/* WSM for the TCI */
|
||||
struct tee_wsm tci;
|
||||
/* The list entry to attach to session list of owner */
|
||||
struct list_head list;
|
||||
/* Session WSMs lock */
|
||||
struct mutex wsms_lock;
|
||||
/* WSMs for a session */
|
||||
struct tee_wsm wsms[MC_MAP_MAX];
|
||||
/* This TA is of Global Platform type */
|
||||
int is_gp;
|
||||
};
|
||||
|
||||
struct tee_session *session_create(struct tee_client *client,
|
||||
const struct mc_identity *identity);
|
||||
static inline void session_get(struct tee_session *session)
|
||||
{
|
||||
kref_get(&session->kref);
|
||||
}
|
||||
|
||||
int session_put(struct tee_session *session);
|
||||
int session_close(struct tee_session *session);
|
||||
|
||||
int session_mc_open_session(struct tee_session *session,
|
||||
struct mcp_open_info *info);
|
||||
int session_mc_cleanup_session(struct tee_session *session);
|
||||
int session_mc_notify(struct tee_session *session);
|
||||
int session_mc_wait(struct tee_session *session, s32 timeout,
|
||||
int silent_expiry);
|
||||
int session_mc_map(struct tee_session *session, struct tee_mmu *mmu,
|
||||
struct mc_ioctl_buffer *bufs);
|
||||
int session_mc_unmap(struct tee_session *session,
|
||||
const struct mc_ioctl_buffer *bufs);
|
||||
int session_mc_get_err(struct tee_session *session, s32 *err);
|
||||
|
||||
int session_gp_open_session(struct tee_session *session,
|
||||
const struct mc_uuid_t *uuid,
|
||||
struct gp_operation *operation,
|
||||
struct gp_return *gp_ret);
|
||||
int session_gp_open_session_domu(struct tee_session *session,
|
||||
const struct mc_uuid_t *uuid, u64 started,
|
||||
struct interworld_session *iws,
|
||||
struct tee_mmu **mmus,
|
||||
struct gp_return *gp_ret);
|
||||
int session_gp_invoke_command(struct tee_session *session, u32 command_id,
|
||||
struct gp_operation *operation,
|
||||
struct gp_return *gp_ret);
|
||||
int session_gp_invoke_command_domu(struct tee_session *session,
|
||||
u64 started, struct interworld_session *iws,
|
||||
struct tee_mmu **mmus,
|
||||
struct gp_return *gp_ret);
|
||||
int session_gp_request_cancellation(u64 slot);
|
||||
|
||||
int session_debug_structs(struct kasnprintf_buf *buf,
|
||||
struct tee_session *session, bool is_closing);
|
||||
|
||||
#endif /* _SESSION_H_ */
|
||||
162
drivers/gud/MobiCoreDriver/tee_client_api.h
Normal file
162
drivers/gud/MobiCoreDriver/tee_client_api.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file corresponds to V1.0 of the GlobalPlatform
|
||||
* TEE Client API Specification
|
||||
*/
|
||||
#ifndef __TEE_CLIENT_API_H__
|
||||
#define __TEE_CLIENT_API_H__
|
||||
|
||||
#include "tee_client_types.h"
|
||||
#include "tee_client_error.h"
|
||||
|
||||
#include "tee_client_api_imp.h"
|
||||
|
||||
#define TEEC_EXPORT
|
||||
|
||||
/*
|
||||
* The header tee_client_api_imp.h must define implementation-dependent types,
|
||||
* constants and macros.
|
||||
*
|
||||
* The implementation-dependent types are:
|
||||
* - teec_context_imp
|
||||
* - teec_session_imp
|
||||
* - teec_shared_memory_imp
|
||||
* - teec_operation_imp
|
||||
*
|
||||
* The implementation-dependent constants are:
|
||||
* - TEEC_CONFIG_SHAREDMEM_MAX_SIZE
|
||||
* The implementation-dependent macros are:
|
||||
* - TEEC_PARAM_TYPES
|
||||
*/
|
||||
|
||||
struct teec_value {
|
||||
u32 a;
|
||||
u32 b;
|
||||
};
|
||||
|
||||
/* Type definitions */
|
||||
struct teec_context {
|
||||
struct teec_context_imp imp;
|
||||
};
|
||||
|
||||
struct teec_session {
|
||||
struct teec_session_imp imp;
|
||||
};
|
||||
|
||||
struct teec_shared_memory {
|
||||
void *buffer;
|
||||
size_t size;
|
||||
u32 flags;
|
||||
struct teec_shared_memory_imp imp;
|
||||
};
|
||||
|
||||
struct teec_temp_memory_reference {
|
||||
void *buffer;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct teec_registered_memory_reference {
|
||||
struct teec_shared_memory *parent;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
union teec_parameter {
|
||||
struct teec_temp_memory_reference tmpref;
|
||||
struct teec_registered_memory_reference memref;
|
||||
struct teec_value value;
|
||||
};
|
||||
|
||||
struct teec_operation {
|
||||
u32 started;
|
||||
u32 param_types;
|
||||
union teec_parameter params[4];
|
||||
struct teec_operation_imp imp;
|
||||
};
|
||||
|
||||
#define TEEC_ORIGIN_API 0x00000001
|
||||
#define TEEC_ORIGIN_COMMS 0x00000002
|
||||
#define TEEC_ORIGIN_TEE 0x00000003
|
||||
#define TEEC_ORIGIN_TRUSTED_APP 0x00000004
|
||||
|
||||
#define TEEC_MEM_INPUT 0x00000001
|
||||
#define TEEC_MEM_OUTPUT 0x00000002
|
||||
|
||||
#define TEEC_NONE 0x0
|
||||
#define TEEC_VALUE_INPUT 0x1
|
||||
#define TEEC_VALUE_OUTPUT 0x2
|
||||
#define TEEC_VALUE_INOUT 0x3
|
||||
#define TEEC_MEMREF_TEMP_INPUT 0x5
|
||||
#define TEEC_MEMREF_TEMP_OUTPUT 0x6
|
||||
#define TEEC_MEMREF_TEMP_INOUT 0x7
|
||||
#define TEEC_MEMREF_WHOLE 0xC
|
||||
#define TEEC_MEMREF_PARTIAL_INPUT 0xD
|
||||
#define TEEC_MEMREF_PARTIAL_OUTPUT 0xE
|
||||
#define TEEC_MEMREF_PARTIAL_INOUT 0xF
|
||||
|
||||
#define TEEC_LOGIN_PUBLIC 0x00000000
|
||||
#define TEEC_LOGIN_USER 0x00000001
|
||||
#define TEEC_LOGIN_GROUP 0x00000002
|
||||
#define TEEC_LOGIN_APPLICATION 0x00000004
|
||||
#define TEEC_LOGIN_USER_APPLICATION 0x00000005
|
||||
#define TEEC_LOGIN_GROUP_APPLICATION 0x00000006
|
||||
|
||||
#define TEEC_TIMEOUT_INFINITE 0xFFFFFFFF
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
|
||||
TEEC_EXPORT u32
|
||||
teec_initialize_context(const char *name, struct teec_context *context);
|
||||
|
||||
TEEC_EXPORT void
|
||||
teec_finalize_context(struct teec_context *context);
|
||||
|
||||
TEEC_EXPORT u32
|
||||
teec_register_shared_memory(struct teec_context *context,
|
||||
struct teec_shared_memory *shared_mem);
|
||||
|
||||
TEEC_EXPORT u32
|
||||
teec_allocate_shared_memory(struct teec_context *context,
|
||||
struct teec_shared_memory *shared_mem);
|
||||
|
||||
TEEC_EXPORT void
|
||||
teec_release_shared_memory(struct teec_shared_memory *shared_mem);
|
||||
|
||||
TEEC_EXPORT u32
|
||||
teec_open_session(struct teec_context *context,
|
||||
struct teec_session *session,
|
||||
const struct teec_uuid *destination,
|
||||
u32 connection_method, /* Should be 0 */
|
||||
const void *connection_data,
|
||||
struct teec_operation *operation,
|
||||
u32 *return_origin);
|
||||
|
||||
TEEC_EXPORT void
|
||||
teec_close_session(struct teec_session *session);
|
||||
|
||||
TEEC_EXPORT u32
|
||||
teec_invoke_command(struct teec_session *session,
|
||||
u32 command_id,
|
||||
struct teec_operation *operation,
|
||||
u32 *return_origin);
|
||||
|
||||
TEEC_EXPORT void
|
||||
teec_request_cancellation(struct teec_operation *operation);
|
||||
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#endif /* __TEE_CLIENT_API_H__ */
|
||||
56
drivers/gud/MobiCoreDriver/tee_client_api_imp.h
Normal file
56
drivers/gud/MobiCoreDriver/tee_client_api_imp.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file defines the implementation-dependent types,
|
||||
* constants and macros for all the Kinibi implementations of the TEE Client API
|
||||
*/
|
||||
#ifndef __TEE_CLIENT_API_IMP_H__
|
||||
#define __TEE_CLIENT_API_IMP_H__
|
||||
|
||||
#define TEEC_MEM_INOUT (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT)
|
||||
|
||||
struct tee_client;
|
||||
|
||||
struct teec_context_imp {
|
||||
struct tee_client *client;
|
||||
};
|
||||
|
||||
struct teec_session_imp {
|
||||
u32 session_id;
|
||||
struct teec_context_imp context;
|
||||
int active;
|
||||
};
|
||||
|
||||
struct teec_shared_memory_imp {
|
||||
struct tee_client *client;
|
||||
int implementation_allocated;
|
||||
};
|
||||
|
||||
struct teec_operation_imp {
|
||||
struct teec_session_imp *session;
|
||||
};
|
||||
|
||||
/*
|
||||
* There is no natural, compile-time limit on the shared memory, but a specific
|
||||
* implementation may introduce a limit (in particular on TrustZone)
|
||||
*/
|
||||
#define TEEC_CONFIG_SHAREDMEM_MAX_SIZE ((size_t)0xFFFFFFFF)
|
||||
|
||||
#define TEEC_PARAM_TYPES(entry0_type, entry1_type, entry2_type, entry3_type) \
|
||||
((entry0_type) | ((entry1_type) << 4) | \
|
||||
((entry2_type) << 8) | ((entry3_type) << 12))
|
||||
|
||||
#endif /* __TEE_CLIENT_API_IMP_H__ */
|
||||
120
drivers/gud/MobiCoreDriver/tee_client_error.h
Normal file
120
drivers/gud/MobiCoreDriver/tee_client_error.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __TEE_CLIENT_ERROR_H__
|
||||
#define __TEE_CLIENT_ERROR_H__
|
||||
|
||||
#define TEEC_SUCCESS ((uint32_t)0x00000000)
|
||||
|
||||
/**
|
||||
* Generic error code : Generic error
|
||||
**/
|
||||
#define TEEC_ERROR_GENERIC ((uint32_t)0xFFFF0000)
|
||||
|
||||
/**
|
||||
* Generic error code : The underlying security system denies the access to the
|
||||
* object
|
||||
**/
|
||||
#define TEEC_ERROR_ACCESS_DENIED ((uint32_t)0xFFFF0001)
|
||||
|
||||
/**
|
||||
* Generic error code : The pending operation is cancelled.
|
||||
**/
|
||||
#define TEEC_ERROR_CANCEL ((uint32_t)0xFFFF0002)
|
||||
|
||||
/**
|
||||
* Generic error code : The underlying system detects a conflict
|
||||
**/
|
||||
#define TEEC_ERROR_ACCESS_CONFLICT ((uint32_t)0xFFFF0003)
|
||||
|
||||
/**
|
||||
* Generic error code : Too much data for the operation or some data remain
|
||||
* unprocessed by the operation.
|
||||
**/
|
||||
#define TEEC_ERROR_EXCESS_DATA ((uint32_t)0xFFFF0004)
|
||||
|
||||
/**
|
||||
* Generic error code : Error of data format
|
||||
**/
|
||||
#define TEEC_ERROR_BAD_FORMAT ((uint32_t)0xFFFF0005)
|
||||
|
||||
/**
|
||||
* Generic error code : The specified parameters are invalid
|
||||
**/
|
||||
#define TEEC_ERROR_BAD_PARAMETERS ((uint32_t)0xFFFF0006)
|
||||
|
||||
/**
|
||||
* Generic error code : Illegal state for the operation.
|
||||
**/
|
||||
#define TEEC_ERROR_BAD_STATE ((uint32_t)0xFFFF0007)
|
||||
|
||||
/**
|
||||
* Generic error code : The item is not found
|
||||
**/
|
||||
#define TEEC_ERROR_ITEM_NOT_FOUND ((uint32_t)0xFFFF0008)
|
||||
|
||||
/**
|
||||
* Generic error code : The specified operation is not implemented
|
||||
**/
|
||||
#define TEEC_ERROR_NOT_IMPLEMENTED ((uint32_t)0xFFFF0009)
|
||||
|
||||
/**
|
||||
* Generic error code : The specified operation is not supported
|
||||
**/
|
||||
#define TEEC_ERROR_NOT_SUPPORTED ((uint32_t)0xFFFF000A)
|
||||
|
||||
/**
|
||||
* Generic error code : Insufficient data is available for the operation.
|
||||
**/
|
||||
#define TEEC_ERROR_NO_DATA ((uint32_t)0xFFFF000B)
|
||||
|
||||
/**
|
||||
* Generic error code : Not enough memory to perform the operation
|
||||
**/
|
||||
#define TEEC_ERROR_OUT_OF_MEMORY ((uint32_t)0xFFFF000C)
|
||||
|
||||
/**
|
||||
* Generic error code : The service is currently unable to handle the request;
|
||||
* try later
|
||||
**/
|
||||
#define TEEC_ERROR_BUSY ((uint32_t)0xFFFF000D)
|
||||
|
||||
/**
|
||||
* Generic communication error
|
||||
**/
|
||||
#define TEEC_ERROR_COMMUNICATION ((uint32_t)0xFFFF000E)
|
||||
|
||||
/**
|
||||
* Generic error code : security violation
|
||||
**/
|
||||
#define TEEC_ERROR_SECURITY ((uint32_t)0xFFFF000F)
|
||||
|
||||
/**
|
||||
* Generic error code : the buffer is too short
|
||||
**/
|
||||
#define TEEC_ERROR_SHORT_BUFFER ((uint32_t)0xFFFF0010)
|
||||
|
||||
/**
|
||||
* Error of communication: The target of the connection is dead
|
||||
**/
|
||||
#define TEEC_ERROR_TARGET_DEAD ((uint32_t)0xFFFF3024)
|
||||
|
||||
/**
|
||||
* File system error code: not enough space to complete the operation.
|
||||
**/
|
||||
#define TEEC_ERROR_STORAGE_NO_SPACE ((uint32_t)0xFFFF3041)
|
||||
|
||||
#endif /* __TEE_CLIENT_ERROR_H__ */
|
||||
|
||||
33
drivers/gud/MobiCoreDriver/tee_client_types.h
Normal file
33
drivers/gud/MobiCoreDriver/tee_client_types.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __TEE_CLIENT_TYPES_H__
|
||||
#define __TEE_CLIENT_TYPES_H__
|
||||
|
||||
/* Definition of an UUID (from RFC 4122 http://www.ietf.org/rfc/rfc4122.txt) */
|
||||
struct teec_uuid {
|
||||
u32 time_low;
|
||||
u16 time_mid;
|
||||
u16 time_hi_and_version;
|
||||
u8 clock_seq_and_node[8];
|
||||
};
|
||||
|
||||
/* Type definition for a TEE Identity */
|
||||
struct tee_identity {
|
||||
u32 login;
|
||||
struct teec_uuid uuid;
|
||||
};
|
||||
|
||||
#endif /* __TEE_CLIENT_TYPES_H__ */
|
||||
596
drivers/gud/MobiCoreDriver/teeclientapi.c
Normal file
596
drivers/gud/MobiCoreDriver/teeclientapi.c
Normal file
|
|
@ -0,0 +1,596 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "tee_client_api.h"
|
||||
#include "mc_user.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "mcinq.h" /* TA termination codes */
|
||||
#include "client.h"
|
||||
|
||||
/* Macros */
|
||||
#define _TEEC_GET_PARAM_TYPE(t, i) (((t) >> (4 * (i))) & 0xF)
|
||||
|
||||
/* Parameter number */
|
||||
#define _TEEC_PARAMETER_NUMBER 4
|
||||
|
||||
/**teec_shared_memory
|
||||
* These error codes are still to be decided by GP and as we do not wish to
|
||||
* expose any part of the GP TAF as of yet, for now they will have to live here
|
||||
* until we decide what to do about them.
|
||||
*/
|
||||
#define TEEC_ERROR_TA_LOCKED 0xFFFF0257
|
||||
#define TEEC_ERROR_SD_BLOCKED 0xFFFF0258
|
||||
#define TEEC_ERROR_TARGET_KILLED 0xFFFF0259
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(operations_wq);
|
||||
|
||||
static void _lib_uuid_to_array(const struct teec_uuid *uuid, u8 *uuid_array)
|
||||
{
|
||||
u8 *identifier_cursor = (u8 *)uuid;
|
||||
/* offsets and syntax constants. See explanations above */
|
||||
#ifdef S_BIG_ENDIAN
|
||||
u32 offsets = 0;
|
||||
#else
|
||||
u32 offsets = 0xF1F1DF13;
|
||||
#endif
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < sizeof(struct teec_uuid); i++) {
|
||||
/* Two-digit hex number */
|
||||
s32 offset = ((s32)((offsets & 0xF) << 28)) >> 28;
|
||||
u8 number = identifier_cursor[offset];
|
||||
|
||||
offsets >>= 4;
|
||||
identifier_cursor++;
|
||||
|
||||
uuid_array[i] = number;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 _teec_to_gp_operation(struct teec_operation *teec_op,
|
||||
struct gp_operation *gp_op)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) {
|
||||
switch (_TEEC_GET_PARAM_TYPE(teec_op->param_types, i)) {
|
||||
case TEEC_VALUE_INPUT:
|
||||
case TEEC_VALUE_INOUT:
|
||||
gp_op->params[i].value.a = teec_op->params[i].value.a;
|
||||
gp_op->params[i].value.b = teec_op->params[i].value.b;
|
||||
break;
|
||||
case TEEC_MEMREF_TEMP_INPUT:
|
||||
case TEEC_MEMREF_TEMP_OUTPUT:
|
||||
case TEEC_MEMREF_TEMP_INOUT:
|
||||
gp_op->params[i].tmpref.buffer =
|
||||
(uintptr_t)teec_op->params[i].tmpref.buffer;
|
||||
gp_op->params[i].tmpref.size =
|
||||
teec_op->params[i].tmpref.size;
|
||||
break;
|
||||
case TEEC_MEMREF_WHOLE:
|
||||
case TEEC_MEMREF_PARTIAL_INPUT:
|
||||
case TEEC_MEMREF_PARTIAL_OUTPUT:
|
||||
case TEEC_MEMREF_PARTIAL_INOUT:
|
||||
gp_op->params[i].memref.offset =
|
||||
teec_op->params[i].memref.offset;
|
||||
gp_op->params[i].memref.size =
|
||||
teec_op->params[i].memref.size;
|
||||
gp_op->params[i].memref.parent.buffer =
|
||||
(uintptr_t)teec_op->params[i].memref.parent->buffer;
|
||||
gp_op->params[i].memref.parent.size =
|
||||
teec_op->params[i].memref.parent->size;
|
||||
gp_op->params[i].memref.parent.flags =
|
||||
teec_op->params[i].memref.parent->flags;
|
||||
break;
|
||||
case TEEC_NONE:
|
||||
case TEEC_VALUE_OUTPUT:
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
gp_op->param_types = teec_op->param_types;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _teec_from_gp_operation(struct gp_operation *gp_op,
|
||||
struct teec_operation *teec_op)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) {
|
||||
switch (_TEEC_GET_PARAM_TYPE(gp_op->param_types, i)) {
|
||||
case TEEC_VALUE_OUTPUT:
|
||||
case TEEC_VALUE_INOUT:
|
||||
teec_op->params[i].value.a = gp_op->params[i].value.a;
|
||||
teec_op->params[i].value.b = gp_op->params[i].value.b;
|
||||
break;
|
||||
case TEEC_MEMREF_TEMP_INPUT:
|
||||
case TEEC_MEMREF_TEMP_OUTPUT:
|
||||
case TEEC_MEMREF_TEMP_INOUT:
|
||||
teec_op->params[i].tmpref.size =
|
||||
gp_op->params[i].tmpref.size;
|
||||
break;
|
||||
case TEEC_MEMREF_WHOLE:
|
||||
break;
|
||||
case TEEC_MEMREF_PARTIAL_INPUT:
|
||||
case TEEC_MEMREF_PARTIAL_OUTPUT:
|
||||
case TEEC_MEMREF_PARTIAL_INOUT:
|
||||
teec_op->params[i].memref.size =
|
||||
gp_op->params[i].memref.size;
|
||||
break;
|
||||
case TEEC_NONE:
|
||||
case TEEC_VALUE_INPUT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u32 _teec_convert_error(int errno)
|
||||
{
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return TEEC_ERROR_ITEM_NOT_FOUND;
|
||||
case EACCES:
|
||||
return TEEC_ERROR_ACCESS_DENIED;
|
||||
case EINVAL:
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
case ENOSPC:
|
||||
return TEEC_ERROR_OUT_OF_MEMORY;
|
||||
case ECONNREFUSED:
|
||||
return TEEC_ERROR_SD_BLOCKED;
|
||||
case ECONNABORTED:
|
||||
return TEEC_ERROR_TA_LOCKED;
|
||||
case ECONNRESET:
|
||||
return TEEC_ERROR_TARGET_KILLED;
|
||||
case EBUSY:
|
||||
return TEEC_ERROR_BUSY;
|
||||
case EKEYREJECTED:
|
||||
return TEEC_ERROR_SECURITY;
|
||||
case ETIME:
|
||||
return TEEC_ERROR_TARGET_DEAD;
|
||||
default:
|
||||
return TEEC_ERROR_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
/* teec_initialize_context: TEEC_SUCCESS, Another error code from Table 4-2 */
|
||||
u32 teec_initialize_context(const char *name, struct teec_context *context)
|
||||
{
|
||||
struct tee_client *client;
|
||||
int ret;
|
||||
(void)name;
|
||||
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
if (!context) {
|
||||
mc_dev_devel("context is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
/* Make sure TEE was started */
|
||||
ret = mc_wait_tee_start();
|
||||
if (ret) {
|
||||
mc_dev_err(ret, "TEE failed to start, now or in the past");
|
||||
return TEEC_ERROR_BAD_STATE;
|
||||
}
|
||||
|
||||
/* Create client */
|
||||
client = client_create(true);
|
||||
if (!client)
|
||||
return TEEC_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
/* Store client in context */
|
||||
context->imp.client = client;
|
||||
|
||||
return TEEC_SUCCESS;
|
||||
}
|
||||
EXPORT_SYMBOL(teec_initialize_context);
|
||||
|
||||
/*
|
||||
* The implementation of this function MUST NOT be able to fail: after this
|
||||
* function returns the Client Application must be able to consider that the
|
||||
* Context has been closed
|
||||
*/
|
||||
void teec_finalize_context(struct teec_context *context)
|
||||
{
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
/* The parameter context MUST point to an initialized TEE Context */
|
||||
if (!context) {
|
||||
mc_dev_devel("context is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The implementation of this function MUST NOT be able to fail: after
|
||||
* this function returns the Client Application must be able to
|
||||
* consider that the Context has been closed
|
||||
*/
|
||||
client_close(context->imp.client);
|
||||
context->imp.client = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(teec_finalize_context);
|
||||
|
||||
/*
|
||||
* If the return_origin is different from TEEC_ORIGIN_TRUSTED_APP, an error code
|
||||
* from Table 4-2. If the return_origin is equal to TEEC_ORIGIN_TRUSTED_APP, a
|
||||
* return code defined by the protocol between the Client Application and the
|
||||
* Trusted Application
|
||||
*/
|
||||
u32 teec_open_session(struct teec_context *context,
|
||||
struct teec_session *session,
|
||||
const struct teec_uuid *destination,
|
||||
u32 connection_method,
|
||||
const void *connection_data,
|
||||
struct teec_operation *operation,
|
||||
u32 *return_origin)
|
||||
{
|
||||
struct mc_uuid_t uuid;
|
||||
struct mc_identity identity = {0};
|
||||
struct tee_client *client = NULL;
|
||||
struct gp_operation gp_op;
|
||||
struct gp_return gp_ret;
|
||||
int ret = 0, timeout;
|
||||
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
gp_ret.value = TEEC_SUCCESS;
|
||||
if (return_origin)
|
||||
*return_origin = TEEC_ORIGIN_API;
|
||||
|
||||
/* The parameter context MUST point to an initialized TEE Context */
|
||||
if (!context) {
|
||||
mc_dev_devel("context is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!context->imp.client) {
|
||||
mc_dev_devel("context not initialized");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
client = context->imp.client;
|
||||
|
||||
if (!session) {
|
||||
mc_dev_devel("session is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
connection_method = TEEC_TT_LOGIN_KERNEL;
|
||||
session->imp.active = false;
|
||||
|
||||
_lib_uuid_to_array(destination, uuid.value);
|
||||
|
||||
memset(&gp_op, 0, sizeof(gp_op));
|
||||
if (operation) {
|
||||
operation->imp.session = &session->imp;
|
||||
ret = _teec_to_gp_operation(operation, &gp_op);
|
||||
if (ret)
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
identity.login_type = (enum mc_login_type)connection_method;
|
||||
|
||||
/* Wait for GP loading to be possible, maximum 30s */
|
||||
timeout = 30;
|
||||
do {
|
||||
ret = client_gp_open_session(client, &uuid, &gp_op, &identity,
|
||||
&gp_ret, &session->imp.session_id);
|
||||
if (!ret || ret != EAGAIN)
|
||||
break;
|
||||
|
||||
msleep(1000);
|
||||
} while (--timeout);
|
||||
|
||||
if (ret || gp_ret.value != TEEC_SUCCESS) {
|
||||
mc_dev_devel("client_gp_open_session failed(%08x) %08x", ret,
|
||||
gp_ret.value);
|
||||
if (ret && ret != -ECHILD)
|
||||
gp_ret.value = _teec_convert_error(-ret);
|
||||
else if (return_origin)
|
||||
/* Update origin as it's not the API */
|
||||
*return_origin = gp_ret.origin;
|
||||
} else {
|
||||
mc_dev_devel(" created session ID %x", session->imp.session_id);
|
||||
session->imp.context = context->imp;
|
||||
session->imp.active = true;
|
||||
if (operation)
|
||||
_teec_from_gp_operation(&gp_op, operation);
|
||||
}
|
||||
|
||||
mc_dev_devel(" %s() = 0x%x", __func__, gp_ret.value);
|
||||
return gp_ret.value;
|
||||
}
|
||||
EXPORT_SYMBOL(teec_open_session);
|
||||
|
||||
u32 teec_invoke_command(struct teec_session *session,
|
||||
u32 command_id,
|
||||
struct teec_operation *operation,
|
||||
u32 *return_origin)
|
||||
{
|
||||
struct tee_client *client = NULL;
|
||||
struct gp_operation gp_op = {0};
|
||||
struct gp_return gp_ret = {0};
|
||||
int ret = 0;
|
||||
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
gp_ret.value = TEEC_SUCCESS;
|
||||
if (return_origin)
|
||||
*return_origin = TEEC_ORIGIN_API;
|
||||
|
||||
if (!session) {
|
||||
mc_dev_devel("session is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!session->imp.active) {
|
||||
mc_dev_devel("session is inactive");
|
||||
return TEEC_ERROR_BAD_STATE;
|
||||
}
|
||||
client = session->imp.context.client;
|
||||
|
||||
if (operation) {
|
||||
operation->imp.session = &session->imp;
|
||||
if (_teec_to_gp_operation(operation, &gp_op))
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
} else {
|
||||
gp_op.param_types = 0;
|
||||
}
|
||||
|
||||
ret = client_gp_invoke_command(client, session->imp.session_id,
|
||||
command_id, &gp_op, &gp_ret);
|
||||
|
||||
if (ret || gp_ret.value != TEEC_SUCCESS) {
|
||||
mc_dev_devel("client_gp_invoke_command failed(%08x) %08x", ret,
|
||||
gp_ret.value);
|
||||
if (ret && ret != -ECHILD)
|
||||
gp_ret.value = _teec_convert_error(-ret);
|
||||
else if (return_origin)
|
||||
/* Update origin as it's not the API */
|
||||
*return_origin = gp_ret.origin;
|
||||
} else if (operation) {
|
||||
_teec_from_gp_operation(&gp_op, operation);
|
||||
}
|
||||
|
||||
mc_dev_devel(" %s() = 0x%x", __func__, gp_ret.value);
|
||||
return gp_ret.value;
|
||||
}
|
||||
EXPORT_SYMBOL(teec_invoke_command);
|
||||
|
||||
void teec_close_session(struct teec_session *session)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tee_client *client = NULL;
|
||||
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
/* The implementation MUST do nothing if session is NULL */
|
||||
if (!session) {
|
||||
mc_dev_devel("session is NULL");
|
||||
return;
|
||||
}
|
||||
client = session->imp.context.client;
|
||||
|
||||
if (session->imp.active) {
|
||||
ret = client_gp_close_session(client, session->imp.session_id);
|
||||
|
||||
if (ret)
|
||||
/* continue even in case of error */
|
||||
mc_dev_devel("client_gp_close failed(%08x)", ret);
|
||||
|
||||
session->imp.active = false;
|
||||
}
|
||||
|
||||
mc_dev_devel(" %s() = 0x%x", __func__, ret);
|
||||
}
|
||||
EXPORT_SYMBOL(teec_close_session);
|
||||
|
||||
/*
|
||||
* Implementation note. We handle internally 2 kind of pointers : kernel memory
|
||||
* (kmalloc, get_pages, ...) and dynamic memory (vmalloc). A global pointer from
|
||||
* a kernel module has the same format as a vmalloc buffer. However, our code
|
||||
* cannot detect that, so it considers it a kmalloc buffer. The TA trying to use
|
||||
* that shared buffer is likely to crash
|
||||
*/
|
||||
u32 teec_register_shared_memory(struct teec_context *context,
|
||||
struct teec_shared_memory *shared_mem)
|
||||
{
|
||||
struct gp_shared_memory memref;
|
||||
struct gp_return gp_ret;
|
||||
int ret = 0;
|
||||
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
/* The parameter context MUST point to an initialized TEE Context */
|
||||
if (!context) {
|
||||
mc_dev_devel("context is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
/*
|
||||
* The parameter shared_mem MUST point to the Shared Memory structure
|
||||
* defining the memory region to register
|
||||
*/
|
||||
if (!shared_mem) {
|
||||
mc_dev_devel("shared_mem is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
/*
|
||||
* The buffer field MUST point to the memory region to be shared,
|
||||
* and MUST not be NULL
|
||||
*/
|
||||
if (!shared_mem->buffer) {
|
||||
mc_dev_devel("shared_mem->buffer is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
if (shared_mem->flags & ~TEEC_MEM_INOUT) {
|
||||
mc_dev_devel("shared_mem->flags is incorrect");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
if (!shared_mem->flags) {
|
||||
mc_dev_devel("shared_mem->flags is incorrect");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
memref.buffer = (uintptr_t)shared_mem->buffer;
|
||||
memref.flags = shared_mem->flags;
|
||||
memref.size = shared_mem->size;
|
||||
ret = client_gp_register_shared_mem(context->imp.client, NULL, NULL,
|
||||
&memref, &gp_ret);
|
||||
|
||||
if (ret)
|
||||
return _teec_convert_error(-ret);
|
||||
|
||||
shared_mem->imp.client = context->imp.client;
|
||||
shared_mem->imp.implementation_allocated = false;
|
||||
|
||||
return TEEC_SUCCESS;
|
||||
}
|
||||
EXPORT_SYMBOL(teec_register_shared_memory);
|
||||
|
||||
u32 teec_allocate_shared_memory(struct teec_context *context,
|
||||
struct teec_shared_memory *shared_mem)
|
||||
{
|
||||
struct gp_shared_memory memref;
|
||||
struct gp_return gp_ret;
|
||||
int ret = 0;
|
||||
|
||||
/* No connection to "context"? */
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
/* The parameter context MUST point to an initialized TEE Context */
|
||||
if (!context) {
|
||||
mc_dev_devel("context is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
/*
|
||||
* The parameter shared_mem MUST point to the Shared Memory structure
|
||||
* defining the memory region to register
|
||||
*/
|
||||
if (!shared_mem) {
|
||||
mc_dev_devel("shared_mem is NULL");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
if (shared_mem->flags & ~TEEC_MEM_INOUT) {
|
||||
mc_dev_devel("shared_mem->flags is incorrect");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
if (!shared_mem->flags) {
|
||||
mc_dev_devel("shared_mem->flags is incorrect");
|
||||
return TEEC_ERROR_BAD_PARAMETERS;
|
||||
}
|
||||
|
||||
shared_mem->buffer = vmalloc(shared_mem->size);
|
||||
if (!shared_mem->buffer)
|
||||
return TEEC_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
memref.buffer = (uintptr_t)shared_mem->buffer;
|
||||
memref.flags = shared_mem->flags;
|
||||
memref.size = shared_mem->size;
|
||||
ret = client_gp_register_shared_mem(context->imp.client, NULL, NULL,
|
||||
&memref, &gp_ret);
|
||||
|
||||
if (ret) {
|
||||
vfree(shared_mem->buffer);
|
||||
shared_mem->buffer = NULL;
|
||||
shared_mem->size = 0;
|
||||
return _teec_convert_error(-ret);
|
||||
}
|
||||
|
||||
shared_mem->imp.client = context->imp.client;
|
||||
shared_mem->imp.implementation_allocated = true;
|
||||
|
||||
return TEEC_SUCCESS;
|
||||
}
|
||||
EXPORT_SYMBOL(teec_allocate_shared_memory);
|
||||
|
||||
void teec_release_shared_memory(struct teec_shared_memory *shared_mem)
|
||||
{
|
||||
struct gp_shared_memory memref;
|
||||
|
||||
/* No connection to "context"? */
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
/* The implementation MUST do nothing if shared_mem is NULL */
|
||||
if (!shared_mem) {
|
||||
mc_dev_devel("shared_mem is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
memref.buffer = (uintptr_t)shared_mem->buffer;
|
||||
memref.flags = shared_mem->flags;
|
||||
memref.size = shared_mem->size;
|
||||
(void)client_gp_release_shared_mem(shared_mem->imp.client, &memref);
|
||||
|
||||
/*
|
||||
* For a memory buffer allocated using teec_allocate_shared_memory the
|
||||
* Implementation MUST free the underlying memory
|
||||
*/
|
||||
if (shared_mem->imp.implementation_allocated) {
|
||||
if (shared_mem->buffer) {
|
||||
vfree(shared_mem->buffer);
|
||||
shared_mem->buffer = NULL;
|
||||
shared_mem->size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(teec_release_shared_memory);
|
||||
|
||||
void teec_request_cancellation(struct teec_operation *operation)
|
||||
{
|
||||
struct teec_session_imp *session;
|
||||
int ret;
|
||||
|
||||
mc_dev_devel("== %s() ==============", __func__);
|
||||
|
||||
ret = wait_event_interruptible(operations_wq, operation->started);
|
||||
if (ret == -ERESTARTSYS) {
|
||||
mc_dev_devel("signal received");
|
||||
return;
|
||||
}
|
||||
|
||||
mc_dev_devel("operation->started changed from 0 to %d",
|
||||
operation->started);
|
||||
|
||||
if (operation->started > 1) {
|
||||
mc_dev_devel("the operation has finished");
|
||||
return;
|
||||
}
|
||||
|
||||
session = operation->imp.session;
|
||||
operation->started = 2;
|
||||
wake_up_interruptible(&operations_wq);
|
||||
|
||||
if (!session->active) {
|
||||
mc_dev_devel("Corresponding session is not active");
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: handle cancellation */
|
||||
|
||||
/* Signal the Trustlet */
|
||||
ret = client_notify_session(session->context.client,
|
||||
session->session_id);
|
||||
if (ret)
|
||||
mc_dev_devel("Notify failed: %d", ret);
|
||||
}
|
||||
EXPORT_SYMBOL(teec_request_cancellation);
|
||||
423
drivers/gud/MobiCoreDriver/user.c
Normal file
423
drivers/gud/MobiCoreDriver/user.c
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm_types.h> /* struct vm_area_struct */
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "mc_user.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "user.h"
|
||||
#include "client.h"
|
||||
#include "mcp.h" /* mcp_get_version */
|
||||
|
||||
/*
|
||||
* Get client object from file pointer
|
||||
*/
|
||||
static inline struct tee_client *get_client(struct file *file)
|
||||
{
|
||||
return (struct tee_client *)file->private_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for system open()
|
||||
* A set of internal client data are created and initialized.
|
||||
*
|
||||
* @inode
|
||||
* @file
|
||||
* Returns 0 if OK or -ENOMEM if no allocation was possible.
|
||||
*/
|
||||
static int user_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tee_client *client;
|
||||
|
||||
/* Create client */
|
||||
mc_dev_devel("from %s (%d)", current->comm, current->pid);
|
||||
client = client_create(false);
|
||||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Store client in user file */
|
||||
file->private_data = client;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for system close()
|
||||
* The client object is freed.
|
||||
* @inode
|
||||
* @file
|
||||
* Returns 0
|
||||
*/
|
||||
static int user_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tee_client *client = get_client(file);
|
||||
|
||||
/* Close client */
|
||||
mc_dev_devel("from %s (%d)", current->comm, current->pid);
|
||||
if (!client)
|
||||
return -EPROTO;
|
||||
|
||||
/* Detach client from user file */
|
||||
file->private_data = NULL;
|
||||
|
||||
/* Destroy client, including remaining sessions */
|
||||
client_close(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check r/w access to referenced memory
|
||||
*/
|
||||
static inline int ioctl_check_pointer(unsigned int cmd, int __user *uarg)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (_IOC_DIR(cmd) & _IOC_READ)
|
||||
#if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE
|
||||
err = !access_ok(VERIFY_WRITE, uarg, _IOC_SIZE(cmd));
|
||||
#else
|
||||
err = !access_ok(uarg, _IOC_SIZE(cmd));
|
||||
#endif
|
||||
else if (_IOC_DIR(cmd) & _IOC_WRITE)
|
||||
#if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE
|
||||
err = !access_ok(VERIFY_READ, uarg, _IOC_SIZE(cmd));
|
||||
#else
|
||||
err = !access_ok(uarg, _IOC_SIZE(cmd));
|
||||
#endif
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for system ioctl()
|
||||
* Implement most of ClientLib API functions
|
||||
* @file pointer to file
|
||||
* @cmd command
|
||||
* @arg arguments
|
||||
*
|
||||
* Returns 0 for OK and an errno in case of error
|
||||
*/
|
||||
static long user_ioctl(struct file *file, unsigned int id, unsigned long arg)
|
||||
{
|
||||
struct tee_client *client = get_client(file);
|
||||
int __user *uarg = (int __user *)arg;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mc_dev_devel("%u from %s", _IOC_NR(id), current->comm);
|
||||
|
||||
if (!client)
|
||||
return -EPROTO;
|
||||
|
||||
if (ioctl_check_pointer(id, uarg))
|
||||
return -EFAULT;
|
||||
|
||||
switch (id) {
|
||||
case MC_IO_HAS_SESSIONS:
|
||||
/* Freeze the client */
|
||||
if (client_has_sessions(client))
|
||||
ret = -ENOTEMPTY;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case MC_IO_OPEN_SESSION: {
|
||||
struct mc_ioctl_open_session session;
|
||||
|
||||
if (copy_from_user(&session, uarg, sizeof(session))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_mc_open_session(client, &session.uuid,
|
||||
session.tci, session.tcilen,
|
||||
&session.sid);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (copy_to_user(uarg, &session, sizeof(session))) {
|
||||
ret = -EFAULT;
|
||||
client_remove_session(client, session.sid);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_OPEN_TRUSTLET: {
|
||||
struct mc_ioctl_open_trustlet trustlet;
|
||||
|
||||
if (copy_from_user(&trustlet, uarg, sizeof(trustlet))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_mc_open_trustlet(client, trustlet.spid,
|
||||
trustlet.buffer, trustlet.tlen,
|
||||
trustlet.tci, trustlet.tcilen,
|
||||
&trustlet.sid);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (copy_to_user(uarg, &trustlet, sizeof(trustlet))) {
|
||||
ret = -EFAULT;
|
||||
client_remove_session(client, trustlet.sid);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_CLOSE_SESSION: {
|
||||
u32 sid = (u32)arg;
|
||||
|
||||
ret = client_remove_session(client, sid);
|
||||
break;
|
||||
}
|
||||
case MC_IO_NOTIFY: {
|
||||
u32 sid = (u32)arg;
|
||||
|
||||
ret = client_notify_session(client, sid);
|
||||
break;
|
||||
}
|
||||
case MC_IO_WAIT: {
|
||||
struct mc_ioctl_wait wait;
|
||||
|
||||
if (copy_from_user(&wait, uarg, sizeof(wait))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = client_waitnotif_session(client, wait.sid, wait.timeout,
|
||||
wait.partial);
|
||||
break;
|
||||
}
|
||||
case MC_IO_MAP: {
|
||||
struct mc_ioctl_map map;
|
||||
|
||||
if (copy_from_user(&map, uarg, sizeof(map))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_mc_map(client, map.sid, NULL, &map.buf);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* Fill in return struct */
|
||||
if (copy_to_user(uarg, &map, sizeof(map))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_UNMAP: {
|
||||
struct mc_ioctl_map map;
|
||||
|
||||
if (copy_from_user(&map, uarg, sizeof(map))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_mc_unmap(client, map.sid, &map.buf);
|
||||
break;
|
||||
}
|
||||
case MC_IO_ERR: {
|
||||
struct mc_ioctl_geterr __user *uerr =
|
||||
(struct mc_ioctl_geterr __user *)uarg;
|
||||
u32 sid;
|
||||
s32 exit_code;
|
||||
|
||||
if (get_user(sid, &uerr->sid)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_get_session_exitcode(client, sid, &exit_code);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* Fill in return struct */
|
||||
if (put_user(exit_code, &uerr->value)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MC_IO_VERSION: {
|
||||
struct mc_version_info version_info;
|
||||
|
||||
ret = mcp_get_version(&version_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (copy_to_user(uarg, &version_info, sizeof(version_info)))
|
||||
ret = -EFAULT;
|
||||
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_INITIALIZE_CONTEXT: {
|
||||
struct mc_ioctl_gp_initialize_context context;
|
||||
|
||||
if (copy_from_user(&context, uarg, sizeof(context))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_gp_initialize_context(client, &context.ret);
|
||||
|
||||
if (copy_to_user(uarg, &context, sizeof(context))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_REGISTER_SHARED_MEM: {
|
||||
struct mc_ioctl_gp_register_shared_mem shared_mem;
|
||||
|
||||
if (copy_from_user(&shared_mem, uarg, sizeof(shared_mem))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_gp_register_shared_mem(client, NULL, NULL,
|
||||
&shared_mem.memref,
|
||||
&shared_mem.ret);
|
||||
|
||||
if (copy_to_user(uarg, &shared_mem, sizeof(shared_mem))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_RELEASE_SHARED_MEM: {
|
||||
struct mc_ioctl_gp_release_shared_mem shared_mem;
|
||||
|
||||
if (copy_from_user(&shared_mem, uarg, sizeof(shared_mem))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_gp_release_shared_mem(client, &shared_mem.memref);
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_OPEN_SESSION: {
|
||||
struct mc_ioctl_gp_open_session session;
|
||||
|
||||
if (copy_from_user(&session, uarg, sizeof(session))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_gp_open_session(client, &session.uuid,
|
||||
&session.operation,
|
||||
&session.identity,
|
||||
&session.ret, &session.session_id);
|
||||
|
||||
if (copy_to_user(uarg, &session, sizeof(session))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_CLOSE_SESSION: {
|
||||
struct mc_ioctl_gp_close_session session;
|
||||
|
||||
if (copy_from_user(&session, uarg, sizeof(session))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_gp_close_session(client, session.session_id);
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_INVOKE_COMMAND: {
|
||||
struct mc_ioctl_gp_invoke_command command;
|
||||
|
||||
if (copy_from_user(&command, uarg, sizeof(command))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = client_gp_invoke_command(client, command.session_id,
|
||||
command.command_id,
|
||||
&command.operation,
|
||||
&command.ret);
|
||||
|
||||
if (copy_to_user(uarg, &command, sizeof(command))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MC_IO_GP_REQUEST_CANCELLATION: {
|
||||
struct mc_ioctl_gp_request_cancellation cancel;
|
||||
|
||||
if (copy_from_user(&cancel, uarg, sizeof(cancel))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
client_gp_request_cancellation(client,
|
||||
cancel.operation.started);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
mc_dev_err(ret, "unsupported command no %d", id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for system mmap()
|
||||
*/
|
||||
static int user_mmap(struct file *file, struct vm_area_struct *vmarea)
|
||||
{
|
||||
struct tee_client *client = get_client(file);
|
||||
|
||||
if ((vmarea->vm_end - vmarea->vm_start) > BUFFER_LENGTH_MAX) {
|
||||
mc_dev_err(-EINVAL, "buffer size %lu too big",
|
||||
vmarea->vm_end - vmarea->vm_start);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Alloc contiguous buffer for this client */
|
||||
return client_cbuf_create(client,
|
||||
(u32)(vmarea->vm_end - vmarea->vm_start),
|
||||
NULL, vmarea);
|
||||
}
|
||||
|
||||
static const struct file_operations mc_user_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = user_open,
|
||||
.release = user_release,
|
||||
.unlocked_ioctl = user_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = user_ioctl,
|
||||
#endif
|
||||
.mmap = user_mmap,
|
||||
};
|
||||
|
||||
int mc_user_init(struct cdev *cdev)
|
||||
{
|
||||
cdev_init(cdev, &mc_user_fops);
|
||||
return 0;
|
||||
}
|
||||
26
drivers/gud/MobiCoreDriver/user.h
Normal file
26
drivers/gud/MobiCoreDriver/user.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _USER_H_
|
||||
#define _USER_H_
|
||||
|
||||
struct cdev;
|
||||
|
||||
int mc_user_init(struct cdev *cdev);
|
||||
static inline void mc_user_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* _USER_H_ */
|
||||
1163
drivers/gud/MobiCoreDriver/xen_be.c
Normal file
1163
drivers/gud/MobiCoreDriver/xen_be.c
Normal file
File diff suppressed because it is too large
Load diff
37
drivers/gud/MobiCoreDriver/xen_be.h
Normal file
37
drivers/gud/MobiCoreDriver/xen_be.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_XEN_BE_H_
|
||||
#define _MC_XEN_BE_H_
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
struct xen_be_map;
|
||||
|
||||
#ifdef CONFIG_XEN
|
||||
int xen_be_init(void);
|
||||
void xen_be_exit(void);
|
||||
#else
|
||||
static inline int xen_be_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void xen_be_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _MC_XEN_BE_H_ */
|
||||
62
drivers/gud/MobiCoreDriver/xen_common.c
Normal file
62
drivers/gud/MobiCoreDriver/xen_common.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_XEN
|
||||
|
||||
#include "main.h"
|
||||
#include "client.h"
|
||||
#include "xen_common.h"
|
||||
|
||||
struct tee_xfe *tee_xfe_create(struct xenbus_device *xdev)
|
||||
{
|
||||
struct tee_xfe *xfe;
|
||||
|
||||
/* Alloc */
|
||||
xfe = kzalloc(sizeof(*xfe), GFP_KERNEL);
|
||||
if (!xfe)
|
||||
return NULL;
|
||||
|
||||
atomic_inc(&g_ctx.c_xen_fes);
|
||||
/* Init */
|
||||
dev_set_drvdata(&xdev->dev, xfe);
|
||||
xfe->xdev = xdev;
|
||||
kref_init(&xfe->kref);
|
||||
xfe->evtchn_domu = -1;
|
||||
xfe->evtchn_dom0 = -1;
|
||||
xfe->irq_domu = -1;
|
||||
xfe->irq_dom0 = -1;
|
||||
INIT_LIST_HEAD(&xfe->list);
|
||||
mutex_init(&xfe->ring_mutex);
|
||||
init_completion(&xfe->ring_completion);
|
||||
return xfe;
|
||||
}
|
||||
|
||||
static void tee_xfe_release(struct kref *kref)
|
||||
{
|
||||
struct tee_xfe *xfe = container_of(kref, struct tee_xfe, kref);
|
||||
|
||||
if (xfe->client)
|
||||
client_close(xfe->client);
|
||||
|
||||
kfree(xfe);
|
||||
atomic_dec(&g_ctx.c_xen_fes);
|
||||
}
|
||||
|
||||
void tee_xfe_put(struct tee_xfe *xfe)
|
||||
{
|
||||
kref_put(&xfe->kref, tee_xfe_release);
|
||||
}
|
||||
|
||||
#endif
|
||||
179
drivers/gud/MobiCoreDriver/xen_common.h
Normal file
179
drivers/gud/MobiCoreDriver/xen_common.h
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2017 TRUSTONIC LIMITED
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MC_XEN_COMMON_H_
|
||||
#define _MC_XEN_COMMON_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <xen/events.h>
|
||||
#include <xen/grant_table.h>
|
||||
#include <xen/xen.h>
|
||||
#include <xen/xenbus.h>
|
||||
|
||||
#include "mc_user.h" /* many types */
|
||||
#include "mciiwp.h"
|
||||
#include "mcimcp.h"
|
||||
#include "mmu.h" /* PMD/PTE max entries */
|
||||
#include "client.h" /* For BE to treat other VMs as clients */
|
||||
|
||||
#define TEE_XEN_VERSION 3
|
||||
|
||||
#define TEE_BUFFERS 4
|
||||
|
||||
enum tee_xen_domu_cmd {
|
||||
TEE_XEN_DOMU_NONE,
|
||||
TEE_XEN_GET_VERSION,
|
||||
/* TEE_XEN_MC_OPEN_DEVICE = 11, SWd does not support this */
|
||||
/* TEE_XEN_MC_CLOSE_DEVICE, SWd does not support this */
|
||||
TEE_XEN_MC_HAS_SESSIONS = 13,
|
||||
TEE_XEN_MC_OPEN_SESSION,
|
||||
TEE_XEN_MC_OPEN_TRUSTLET,
|
||||
TEE_XEN_MC_CLOSE_SESSION,
|
||||
TEE_XEN_MC_NOTIFY,
|
||||
TEE_XEN_MC_WAIT,
|
||||
TEE_XEN_MC_MAP,
|
||||
TEE_XEN_MC_UNMAP,
|
||||
TEE_XEN_MC_GET_ERR,
|
||||
/* TEE_XEN_GP_INITIALIZE_CONTEXT = 21, SWd does not support this */
|
||||
/* TEE_XEN_GP_FINALIZE_CONTEXT, SWd does not support this */
|
||||
TEE_XEN_GP_REGISTER_SHARED_MEM = 23,
|
||||
TEE_XEN_GP_RELEASE_SHARED_MEM,
|
||||
TEE_XEN_GP_OPEN_SESSION,
|
||||
TEE_XEN_GP_CLOSE_SESSION,
|
||||
TEE_XEN_GP_INVOKE_COMMAND,
|
||||
TEE_XEN_GP_REQUEST_CANCELLATION,
|
||||
};
|
||||
|
||||
enum tee_xen_dom0_cmd {
|
||||
TEE_XEN_DOM0_NONE,
|
||||
TEE_XEN_MC_WAIT_DONE = TEE_XEN_MC_WAIT,
|
||||
TEE_XEN_GP_OPEN_SESSION_DONE = TEE_XEN_GP_OPEN_SESSION,
|
||||
TEE_XEN_GP_CLOSE_SESSION_DONE = TEE_XEN_GP_CLOSE_SESSION,
|
||||
TEE_XEN_GP_INVOKE_COMMAND_DONE = TEE_XEN_GP_INVOKE_COMMAND,
|
||||
};
|
||||
|
||||
union tee_xen_mmu_table {
|
||||
/* Array of references to pages (PTE_ENTRIES_MAX or PMD_ENTRIES_MAX) */
|
||||
grant_ref_t *refs;
|
||||
/* Address of table */
|
||||
void *addr;
|
||||
/* Page for table */
|
||||
unsigned long page;
|
||||
};
|
||||
|
||||
struct tee_xen_buffer_info {
|
||||
/* Page Middle Directory, refs to tee_xen_pte_table's (full pages) */
|
||||
grant_ref_t pmd_ref;
|
||||
/* Total number of refs for buffer */
|
||||
u32 nr_refs;
|
||||
u64 addr; /* Unique VM address */
|
||||
u32 offset;
|
||||
u32 length;
|
||||
u32 flags;
|
||||
u32 sva;
|
||||
};
|
||||
|
||||
/* Convenience structure to get buffer info and contents in one place */
|
||||
struct tee_xen_buffer {
|
||||
struct tee_xen_buffer_info *info;
|
||||
union tee_xen_mmu_table data;
|
||||
};
|
||||
|
||||
struct tee_xen_ring {
|
||||
/* DomU side, synchronous and asynchronous commands */
|
||||
struct {
|
||||
enum tee_xen_domu_cmd cmd; /* in */
|
||||
u32 id; /* in (debug) */
|
||||
/* Return code of this command from Dom0 */
|
||||
int otherend_ret; /* out */
|
||||
struct mc_uuid_t uuid; /* in */
|
||||
u32 session_id; /* in/out */
|
||||
/* Buffers to share (4 for GP, 2 for mcOpenTrustlet) */
|
||||
struct tee_xen_buffer_info buffers[TEE_BUFFERS]; /* in */
|
||||
/* MC */
|
||||
struct mc_version_info version_info; /* out */
|
||||
u32 spid; /* in */
|
||||
s32 timeout; /* in */
|
||||
s32 err; /* out */
|
||||
/* GP */
|
||||
u64 operation_id; /* in */
|
||||
struct gp_return gp_ret; /* out */
|
||||
struct interworld_session iws; /* in */
|
||||
} domu;
|
||||
|
||||
/* Dom0 side, response to asynchronous command, never read by Dom0 */
|
||||
struct {
|
||||
enum tee_xen_dom0_cmd cmd; /* in */
|
||||
u32 id; /* in (debug) */
|
||||
/* Return code from command */
|
||||
int cmd_ret; /* in */
|
||||
/* The operation id is used to match GP request and response */
|
||||
u64 operation_id; /* in */
|
||||
struct gp_return gp_ret; /* in */
|
||||
struct interworld_session iws; /* in */
|
||||
/* The session id is used to match MC request and response */
|
||||
u32 session_id; /* in */
|
||||
} dom0;
|
||||
};
|
||||
|
||||
struct tee_xfe {
|
||||
struct xenbus_device *xdev;
|
||||
struct kref kref;
|
||||
grant_ref_t ring_ref;
|
||||
int pte_entries_max;
|
||||
int evtchn_domu;
|
||||
int evtchn_dom0;
|
||||
int irq_domu;
|
||||
int irq_dom0;
|
||||
struct list_head list;
|
||||
struct tee_client *client;
|
||||
struct work_struct work;
|
||||
/* Ring page */
|
||||
union {
|
||||
unsigned long ring_ul;
|
||||
void *ring_p;
|
||||
struct tee_xen_ring *ring;
|
||||
};
|
||||
/* Buffer pages */
|
||||
struct tee_xen_buffer buffers[TEE_BUFFERS];
|
||||
struct mutex ring_mutex; /* Protect our side of ring */
|
||||
struct completion ring_completion;
|
||||
int ring_busy;
|
||||
/* Unique ID for commands */
|
||||
u32 domu_cmd_id;
|
||||
};
|
||||
|
||||
struct tee_xfe *tee_xfe_create(struct xenbus_device *xdev);
|
||||
static inline void tee_xfe_get(struct tee_xfe *xfe)
|
||||
{
|
||||
kref_get(&xfe->kref);
|
||||
}
|
||||
|
||||
void tee_xfe_put(struct tee_xfe *xfe);
|
||||
|
||||
static inline void ring_get(struct tee_xfe *xfe)
|
||||
{
|
||||
mutex_lock(&xfe->ring_mutex);
|
||||
xfe->ring_busy = true;
|
||||
}
|
||||
|
||||
static inline void ring_put(struct tee_xfe *xfe)
|
||||
{
|
||||
xfe->ring_busy = false;
|
||||
mutex_unlock(&xfe->ring_mutex);
|
||||
}
|
||||
|
||||
#endif /* _MC_XEN_COMMON_H_ */
|
||||
1194
drivers/gud/MobiCoreDriver/xen_fe.c
Normal file
1194
drivers/gud/MobiCoreDriver/xen_fe.c
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue