hawao push for android 12

* tag 'MMI-S2SE32.28-28-4' of https://github.com/MotorolaMobilityLLC/motorola-kernel-modules: (2402 commits)
  dlkm: vibrator: No vibration when media controls working.
  hawao+oled: recharger change level to 98%,200mv
  Add kalama in Build_external_kernelmodule.mk
  not notify current changes
  charger: change the adc value unit and add slave iio
  charger: Rename bq2597x to cp
  devon:optimize charGing
  hawao+oled: fg 1st(cw2217) change to 99 ,2nd(sm5602) change to 98
  charger:remove read permission
  charger:increase wait_time_out
  charger:add I2C 32bits addr interface
  Rhodec: Uptade chipone_tddi_fhd_mmi driver
  sm5350_bl: support current align configuration for exp mode
  devon:charger bring up
  Devon:Bringup Egistec FPS ETS_617
  Devon:Bringup Egistec FPS ETS_617
  charGing: delete redundant code
  FG: add fake battery in mm8013
  Notify mmi_battery change when combo current change
  dlkm: leds/aw2033: add initial version driver
  oneli: update wl2864c makefile
  Fix parade touch driver build eror
  Add CLI touch feature of single tap to wakeup device
  rhode: input touch focal add reset
  rhode: input touch fix factory test
  charger:cps4019 fix build errors
  charger:Add Android.mk and Kbuild files
  charger:cps4019 wireless charGing bring up
  devon:charger bring up
  add chipone single tap and cqatest function
  chipone single tap function
  sgm41513:add sgm41513 chipid detect,
  devon:charger bring up
  sgm4154x:add sgm41513 macro control.
  devon:charger bring up
  devon:charger bring up
  devon:charger bring up
  Change CLI touch test limits file name format
  Bringup FPC Fingerprint
  Bringup FPC Fingerprint
  driver/egis: Update Makefile to make the building compatible
  Add CLI touch sysfs factory test node
  Hawao+oled: the 2nd fg(sm5602)set 97% to 100%
  Comment out unused CLI sysfs node code
  PD: apply sc2150A vendor patch
  Revert "(CR): PD: apply sc2150A vendor patch"
  Adjust the paired battery policy
  charGing: update HW current max to userspace
  charGing: improve recharge batt voltage after stop
  PD: apply sc2150A vendor patch
  charGing:	add device node support
  charGing:	add iio sys support
  fg: bring up fuel gauge mm8013
  charGing: charger pump quit when vbat < chrg_step_cv_volt
  rhode: input touch fix build id
  dlkm: chipone_tddi_fhd: add initial chipone fhd tddi driver
  EQS: Fix for compile.
  Fix system crash when main touch uses LDOC13
  charGing: add bq25960 gpio irq
  charGing: add bq25960 logs
  Add ibat polority invert from dt
  Create rbs fod driver for dubai
  devon:sensors bringup
  Revert FOD change from capacitive rbs
  remove unused dependence sym ref
  Fix fod event not working during touch resume
  charGing: modify ss_timeout to max value
  Hawaii+ OLED:fpsensor chipone enable power control
  [Hawaii+Oled]Remove the print function of fingerprint hard interrupt EGISFP
  Fix relay build dependence
  dlkm: sm5350_bl: add default brightness config
  charge : fix read VBUS error cause charGing power climb slow issue
  optimize battery charGing policy
  devon:sensors bringup
  Add relay between fps and touch
  Trigger fod event only when FOD is enabled
  Add CLI touch factory test cp_panel node
  charGing: rename filename and fix build errors
  fg: fixed sm5602 can't update info immediately
  Li:capsensor factory bringup
  Change config_id format for FW upgrade
  dubai: vibrator fix rtp issue
  FG: Add mm8013c driver code
  charGing: add bq25960 driver
  charger: fix qc3p rerun bc1.2 fail w/ some qc3.0 adpter
  charGing: Default using HW ILIM for bq25890 and SC89890
  Fix "active_panel" undefined build error.
  Fix build errors with missing header files.
  dlkm: sm5350: get map mode from dtsi
  Change parade touch driver header files path
  Add CLI parade touch suspend/resume logic
  Support receiving dual-screen notifiactions.
  charGing: sometime main charger can't recovery to max icl
  smart_pen_charger: Update pen soc to ADSP
  The whole charGing process will go twice CV
  let hal to control the log level
  Add HW chip detect mode for ST NFC
  rhode: update charger mode & ito test result
  Revert "(CR) nova_0flash_mmi: log touch down event timestamp"
  Add module parameter to enable panic on delta_vbat
  cyttsp5_core.c: Import namepace for "kernel_read"
  Change CLI touch test return data format
  Revert "(CR): SM5602_FG: apply patch-version: 20220212_00"
  dynamic adjust battery voltage
  charGing: fixed build error
  Rhode: Modify ITO test way from "echo" to "cat"
  charGing: fixed build error
  charGing: add qc3p sw detected for sc89890H
  SM5602_FG: apply patch-version: 20220212_00
  dlkm: vibrator: Support camera actuator noise reduction
  Rhode: TP: focal ic bringup: driver files upload
  PD: add sc2150A vendor patch
  charGing: clear charger type variable
  charGing: ignore setting qc icl when pd active
  Set paired battery discharge load based paired_ichg
  Add CLI touch sysfs node for factory test
  nova_0flash_mmi: log touch down event timestamp
  modules:sc89890h:bringup second master charger otg
  charGing: remove the vbat condition which enable 33W
  charGing: increase qc3p power judge threshold
  modules:Second charger could recharge after report full charge
  charGing: report power max 30W when usb type is qc3p
  sar:add driver for multi sx937x
  sar: update the driver of aw9610x to v0.9.3
  FG: SM5602 FG add cycle_count/fcc_design/counter property
  Update zero tap down and up logic
  rhode: solve the problem of slow power-off of front dvdd
  Support a main battery power supply
  Fix cyttsp5 driver crash
  add paired battery info notification
  add paired id to support paired battery notification
  ignore optional dts property exist check result
  Use touch mmi class method to do FW upgrade
  Support touchscreen class in the parade drvier code.
  Hawaii+oled: chipone fingerprint disable power control
  qc3p beGing charGing voltage to 3V
  slow attach 33w change code buld fail
  Solve the problem of sending command conflicts
  chg: fix qc3p rerun bc1.2 fail w/ some qc3.0 adpter
  charGing: slow attach usb will not charGing as 33w
  charGing: avoid I2C bus pending
  rhodec: fix compile error for ILI7807S
  modules: Can't show turbo icon for quickly plugout/in usb
  rhodec:Bringup touch TXD ILI7807S
  Use of_ API to parse gpio pin number
  Fix parade tma5xx touch driver compiling error
  Add initial parade touch driver code
  charGing: set icl 1.5A when type_c mode is medium
  rhode: long time vibration notice camera
  gauge: force set sm5602 temperature to 25 degree
  modules: vision station WHITE MASK test failed
  FG: Rt9426a fg add soc ir comp
  charGing: avoid icl =100ma for 33w charger
  FG: sm5602 fg support muti-battery parameter
  DLKM/fps:optimise focal driver
  charGing: add pd support judge
  HAWAO+OLED: chipone fingerprint modify the power-on sequence
  gauge: force batt present in bt station for factory test
  ibiza:disable power control for chipone FPS
  input: goodix_v1510: fix irq wake disable unbalanced issue
  Support screen callback thru panel notification
  use correct macro for FOD
  discrete:optimize charging enabled interface
  qti_glink_charger: Add wireless dump info
  smart_pen_charger: Add power_supply_changed when receive PEN event
  charging: report usb present when pd hard reset
  Bringup sgm4154x charger driver to Moto product
  Add initial sgm4154x driver code from vendor
  FPS: Modify the power-on sequence for focaltech
  2snd Fuelgauge SM5602 move temp coefficient to ext channel
  2snd Fuelgauge SM5602 current direct set and coefficient
  sm5602:set battery temp to 25 in moto-factory mode
  cw2217b:set battery temp to 25 in moto-factory mode
  Add watchdog_cpu_dump and tzlog_dump support on 5.4
  Hawao:Enable power gpio for Chipone FPS
  DLKM/fps:add build control for focal
  charging: fixed 15W product build error
  charging: add SW QC3+ 33W turbo charging
  goodix: Open the touch esd check function
  charging: add mutex lock init
  power:fet_control:  Apply main batt ocv instead of v_now
  charging: avoid i2c error when system resume and suspend
  DLKM/fps:optimise screen status interface for focal
  modules:regulator_vibartor:add regulator long short voltage logic.
  lock mode switching operations for thread synchronization.
  sar: support sar reset after usb hot-plug
  dlkm:Update rbs driver for FOD
  mmi_annotate: add unpersist annotate memory region to minidump
  focaltech_v3_mmi:add touchscreen mmi to improve FW logic
  goodix:  Modify the report rate command.
  dubai: vibrate add short index
  Increase the number of I2C retries.
  dubai: input touch add wait pm
  qc3p:set qc3p charging voltage threshold 3.4V
  Change to mdelay for power up delay
  Adjust bq27426 fg module to support Android S
  Revert (CR):sgm4154x:add hawao vbus adc sample ratio
  wt6670: add QC3P firmware num node to /proc/driver
  DLKM/fps:optimise power ctl for focal
  FG: apply rt9426a 0015 patch
  rhodep:open charger mode;
  charging: enable irq wake up
  charging: optimize sgm41542 charging type detected
  Revert "(CR):add firmware interface"
  bq2589x:add qc3p charger type
  sgm:add qc3p charger type
  backlight/sm5350: Add a sm5350 IC which is second backlight IC
  charging: avoid to report usb type before apsd done
  charging: add icl*vbus to report power max
  charger: compatible NU2105 Charge Pump in bq2597x
  Reset charger configure and constraint for glink down
  FG: rt9426a add cancel_work when system into suspend
  wt6670: add retry function to wt6670 read fw
  Update Goodix TP FOD event implementation
  qc3p:optimize power supply init
  mmi_annotate: write info mem when persist mem unsupported
  [hawaii+oled]:vibrator long short vibration intensity 1/2
  smart_pen_charger: Add PEN_STAT_CHARGE_DISABLED
  mod the sc89890h register config
  Rhode:use regulator interface to control vbus
  dubai: input touch add gki support
  bq:add enable termination function
  FG: RT9426a add safeguard for SOH
  FG: RT9426A add safeguard for ocv_index
  goodix: Solve cpu_latency_qos_add_request called warning
  input: goodix_v1510: add charging and gtp-tools node
  PD: config cc status for sc2150 after shutdown
  Do not re-enable the stylus when the stylus mode is switched
  mmi_charger:use orderly shutdown in factory mode
  sgm4154x:add hawao vbus adc sample ratio
  qc3p:limit ibus 300mA for main charger no ibus adc
  charging: Ignore the PD vote icl unless BC1.2 done
  add charger ic - sc89890h config
  modules:add sm5602 patch to fix the soc jump problem
  FG: Modify SM5602 FG first update battery data time to 50ms
  add force_chg_auto_enable interface
  add firmware interface
  Disable irq before entering gesture mode and IC resetting.
  input: goodix_v1510: enable single tap function
  Support resend stylus cmd after abnormal resetting.
  Record and restore the value of the mode
  QC3P:use qc3p config distinguish other not qc3p project
  QC3P:detection and iio sys support
  z350: add 2nd z350 qc logic
  sgm:add enable termination function
  discrete:add enable termination iio definition
  qc3p:add main charger termination ctl algorithm
  QC3P:turbo charger module return value error
  dubai: vibrator add haptic rtp
  qc3p:optimize turbo charging current
  charging: add ibat monitor work
  fg: get current in real time
  sm5602: fix kernel panic when the device resume
  PEN_HALL:disable wakeup source
  fs:exfat-linux:Integrate exfat-linux open source driver
  fg/sm5602: Early detection device chipID
  fg/sm5602: correct the unit of vbat and temp
  sc89890h:device: add sc89890h device id
  focaltech_v3_mmi: rename .i file to fix potential build error
  focaltech_v3_mmi: enable firmware download
  focaltech_v3_mmi: add sys touch node & info
  module/input:hawao: add focaltech_v3_mmi for i2c flash
  Rhode 5GP: add wt6670 get firmware num node
  dubai: input touch update fw 21120803
  DLKM/fps:add power ctrl for ets kernel driver
  pd: bring up sc2150a
  power:fet_control Balance ckt Fet Close Batt2 over 200mV
  wt6670: wake up wt6670 when get version num
  sm5602: Add new fgauge sm5602
  QC3P:iio sys channel add kernel version
  QC3P:module name change to mmi_discrete_turbo_charger
  QC3P:discrete turbo charger
  FG:dynamic config sns resistance
  QC3P:detection and iio sys support
  QC3P:iio sys channel
  QC3P:add iio sys support
  QC3P:ADC algorithm compatible
  QC3P:copy of module mmi_parallel_charger_iio for mmi_discrete_turbo_charger
  goodix: support film sensitivity function.
  Support report rate switch in RoguePRC
  dubai: vibrator add long brk
  PD: Pick Richtek patch13 to improve irq response speed
  PD:Revert "(CR):rt1715:add debugfs interface for dump reg"
  PD: pick Richtek patch12
  FG: power_supply_property get rt9426a battery info from local value
  dubai: input touch support fod
  sar: aw9610: Modify sar sensor voltage.
  moto regulator vibrator: fix null pointer bug
  charging: config ICL after BC1.2 done
  qti_glink_charger: Add wls_notify_callback
  qti_glink_charger: Add folio_mode
  qti_glink_charger: Add wls input current limit for thermal
  PD: config pd active is inactive status after cable plug out
  wt6670f_qc3p: qc3p wt6760 isp download function
  charging: add sem lock to protect dpdm detected
  FG:cw2217 Remove the redundant msleep in the read i2c operation
  FROMGIT: usb: gadget: f_mass_storage: Disable eps during disconnect
  sar: aw9610: the reference channel can't be seen on user layer
  bias/ocp2138:add support ocp2138 bias ic config
  fg: cw2217 add ui_soc feature
  IC needs to confirm the device within 50ms after power-on
  No need to send the suspend command when power off.
  Revert "(CR):power: cw2217 add ui_soc feature"
  bq2597x: bq2597x cp_enable interface does not work
  dlkm: sec_nfc: add samsung nfc driver
  dlkm: sd77426 driver add soh api
  power: cw2217 add ui_soc feature
  goodix_v1510_mmi: check if panel is available
  focaltech_v2_mmi: check if panel is available
  bms: do battery power supply change when fg update data
  Power: remove "\n" about mmi_discrete showing factory test node
  charging: add charge counter prop
  kernel:export interface tcpm_inquire_typec_remote_rp_curr
  charging: modify heartbeat cycle to 100s
  bms: modify battery capacity unit from mah to uah
  charging: remove useless power supply prop
  charging: remove FULL status in charging ic driver
  power:fet_control ironmn: ps prop usb type BRICKID
  regulator: Force shutdown all regulators when system power off.
  charging: stop charging when capacity > upper_limit
  charging: fixed otg boost current limit error
  Add pm_qos control to reduce the interrupt responce time
  wl2868c: modify ldo2 and ldo6 default voltage
  charging: fixed the error HVDCP voltage standard
  charging: set register 3250ma when icl >=3A
  smart_pen_charger: Add pen_status and pen_error notify
  qti_glink_charger: Add tx_mode and rx_connected sysfs node
  PD: Extend PD adapter class charge function
  charge: add chage&PD policy
  power: rt9426a update fcc design
  Rhode4G: Implement different ldo compatibility.
  Revert "(CR): Add pm_qos control to reduce the interrupt responce time"
  rhodep: camera power IC et5907 config
  Add calibration noise error handler
  Add pm_qos control to reduce the interrupt responce time
  rhode: rhode 4G camera ldo config[2/3]
  charging: fixed uevent block issue
  mmi_qc3p_wt6670f: int pin config to input mode
  Revert "(CR): Revert "Revert (CR) power:fet_control Vbatt2 balance path""
  Revert "(CR) power:fet_control Remove ps_notification cb"
  qpnp_adaptive:  Change Makefile to compatible with Android S build
  dlkm: Add check if panel is available for gt9916s
  power: fix rt9426a get the negative soc or soc jump
  Wallet shown slow when screenoff at qingdao
  mmi_qc3p_wt6670f: remove QC3.5 detection in shutdown ops
  power:fet_control Remove ps_notification cb
  Revert "Revert (CR) power:fet_control Vbatt2 balance path"
  charging: update charging status when power supply get
  support pmk8350 gpio3 as stylus clk.
  charging: use msleep instead of mdelay
  dlkm: silead_fps: remapping the keycode
  charging: defined default termination current 120ma
  charging: optimize ICO feature
  charging: schedule monitor work when charging
  charging: enable absolute vindpm
  support goodix edge suppression function.
  goodix: Support interpolation game mode function.
  stmicro: Add the judgment of the sensitivity mode value.
  dlkm: nfc: alway probe nfc driver
  mmi_qc3p_wt6670f: fix wt6670f driver bug
  Revert "(CR) power:fet_control Vbatt2 balance path"
  charging: optimize otg current setting logic
  Support leather mode setting.
  charging: fixed sometimes don't request dpdm issue
  sar: modify the vdd name for aw9610.
  mmi_charger: align gki 2.0 requirement
  charging: enable ILIM pin for bq25890H
  bq2597x_mmi_iio: add bq2597x gki and irq code
  bq2597x_mmi_iio: add bq2597x initial code
  mmi_qc3p_wt6670f: add qc3p logic initial code
  mmi_discrete_charger: modify enum redefined
  sgm4154x_chg_mmi: changed chg_en_pin judgment logic
  mmi_charger: modify enum redefined
  charging: fixed unit can't connected PC sometimes
  milan5G: sar sensor driver
  sar: add aw9610 driver
  charging: avoid vbus voltage increase to 12V
  charging: add qc3.0 arch for bq25890H
  power:fet_control  Vbatt2 balance path, chrg fullcurr, ocp
  charge: add iio interface for typeC mode
  typeC: add iio interface for typeC mode
  charging: config charger power supply type unknown
  mmi_charger:use orderly shutdown in factory mode
  Close the stylus clock when system suspend.
  charging: avoid kernel panic
  Fix path for ion lib in Kernel 4.19
  power: early register bms_phy client for cw2217_fg
  Update Trustonic TEE driver module to version 410a-V108
  pstar: input touch add delay for mode
  Power: update fg current orientation
  charging: fixed get vbus voltage error
  charging: add VREG fine tunning for setting float voltage
  charging: replace numeric value with macros
  charging: adjust sgm41542 prob sequence
  sgm7220: add typeC mode switch interface for sgm7220
  tcpc: add typeC mode switch interface
  sgm7220: Reorganize sgm7220 driver architecture
  Support goodix single tap to wake the device
  power: modified the caculation formula of ocv_checksum_dtsi
  charging: add qc3.0 charging feature
  sr100: Increase delay range on read/write
  goodix: support charger mode switching.
  stmicro: support touch film sensitivity switching
  milanf: dlkm: Support game mode by defualt on nova_0flash_mmi
  fts: deleate the PM_WAKEUP_EVENT in interrupt threaded
  DLKM: UI batt level show 100% when real batt level 0%
  Suspend and resume process using touchscreen class.
  power: cw2217b user_rsense modify to 10mhom
  charging: disable HVDCP detected
  Solve goodix tp firmware upgrade problem
  pct1812_mmi: no touch zones
  print ulog when write oem glink command
  bq25890: set input voltage limit 4600mv
  charging: setting icl=0 for usb suspend
  power: create chg_type node for factory test
  touchscreen_mmi: Support refresh reate notification in Rogue
  dubai: touch use gki api
  dlkm: fix vibrator stop fail issue
  typeC: add typec cc orientation interface
  typec: add host and device interface
  charging: add iio psy to provide api for other modules
  dlkm: rt9426a fg update read fg_extreg_table
  Cypfq: dlkm: Bringup touch CSOT ILI7807S
  dlkm: rt9426a fg update rt9426a_read_page_cmd
  charging: fixed user version build error for bq25890H
  wls-chrg: Add FOD_CURR and FOD_A/FOD_B settings API
  charging: dcp current base on dtsi config in mmi charger
  charger/bq25890: improve charger BC1.2 detection & rerun apsd
  typeC: import correct Modules.symvers in GKI build mode
  dlkm: Add focaltech fps module
  dubai: vib update file name
  power: discrete charger policy create force_chg_fail_clear
  dubai:input touch init setup
  Update goodix berlin touchscreen driver to v1.2.3
  Power: discrete_charger policy create factory tcmd test node
  charging: optimize bc1.2 detect for sgm41542
  DLKM/sar:optimise sx9375 driver on bo
  power: add get charing current api for discrete_charger_class
  power:fet_control: ironmn:  Fix typo in chrg-fullcurr-en-gpio
  dlkm: add new regulator vibrator driver
  hall:fix not report issue
  pct1812_mmi: custom config options
  pct1812_mmi: report scroll gestures
  pd: add PD20/PD30 interface for tcpc
  pd: create pd adapter class
  charger/bq25890:export api for charger class
  goodix: support stylus mode switching.
  power: discrete charging solution intial version TCMD driver
  backlight: fix brightness flash issue
  Upgrade egis driver for supporting ET721
  charging: fixed charging policy kernel panic
  Modify the config id and build id
  charger/bq25890: stop ADC when system shutdown
  sgm41542: provide some api for  charger class
  charging: add discrete ics charging policy
  power: cw2217 fg's name rename to bms from battery
  power: rt9426a fg's name rename to bms from battery
  power: cw2217 fg add fcc/fcc_design info
  dubai: vibrator init setup
  power: rt9426a_fg introduce supprot muti-battery parameter func
  power: cw2217 introduce mutil-battery parameter func
  UWB: support build in kernel-5.10
  sx937x:enable usb cal
  Charger/bq25890: force 5V/2A charging
  power: cw2217 fg update battery parameter
  power: rt9426a register charger_full node
  power: apply 1th battery parameter for CW2217 fg
  power:fet_control: Add logic for ironmn EVB HW 2nd Batt
  charger/bq25890:disable maxcharge handshake
  charger/bq25890:pull charge enable pin low when charge
  Revert "(CR) build: Only include modules needed by product"
  charger/rt9467:remove MMI_STOPSHIP change.
  Add a new DLKM to support smart pen charger driver
  Add OEM glink notification and notifier call chain
  charger/bq25890: add usb properties
  Resolve compilation errors for unused functions
  qti_glink_charger: extend addr&data to UINT32
  typc: Open macro for TCPC
  charging: force 5V/2A
  solve the kobject_get_path undefined in gki build
  Only save test file in factory build
  sgm7220: add sgm7220 pwr interface
  charger/bq25890: force D+/D- detection for APSD
  typec: Open pd dbg info
  Does not distinguish 9916 and 9916P touch chips.
  touchscreen: dynamic check the goodix device status
  touchscreen: dynamic check the stmicro device status
  mmi_info: provide check dynamic device status interface
  xpeng: input touch add wait when single tap
  mmi_info: Get bootconfig from device tree
  utags: read utag block device path in bootconfig from device tree
  utags: Use bio to replace kernel_read and write API
  charger/bq25890:add BC1.2 feature
  rt1715:add debugfs interface for dump reg
  power/charger: add init bq25890 driver
  power: rt9426a_fg add soh api
  power: introduce fuel gauge cw2217b driver
  backlight: support backlight current align configuration for exp mode
  backlight: add lm3697 map type for linear or exponential mode
  backlight: add aw99703 map type for linear or exponential mode
  goodix: support factory tcmd test.
  goodix: update test file to 1.2.2 version
  charging: detect hw chip id before do any action
  Power: enable rt9426a fg parse dts function
  power/charger: add walkaround to fix REGN side effect
  power/charger: add free IRQ before reset
  goodix: enable stylus clock on Rogue project.
  charging: add HVDCP detected function
  backlight: don't enable aw99703 backlight while brighness level 0
  pct1812_mmi: properly fail probe on i2c error
  Revert "(CR): mmi_charger: Fix battery_supply_init, batt_psy."
  power/charger: add rt9467 charger driver
  mmi_charger: Fix battery_supply_init, batt_psy.
  Support loadding multiple config files
  build: Only include modules needed by product
  pct1812_mmi: control debug messages
  pct1812_mmi: added input device
  pct1812_mmi: firmware update feature
  dlkm: add new silead fps driver
  Add sysfs to support force disable charging
  qti_glink: Add sysfs node to get wireless chip id
  add lpd and vbus present state
  Send LPD and VBUS uevent for LPD alarm feature
  vibrator: add new ldo vibrator driver
  Support touchscreen calss in the goodix drvier code.
  qti_glink_charger: move WLS property types to the end
  GKI kernel build upgrade
  build: Only include modules needed by product
  set screen default state to on
  dlkm: sd77426 modify battery NTC table
  fet_control: Add fet_control driver, full_current_en
  charging: add usb property
  charging: update sgm41542 driver V002
  usb: mass_storage: Add support for SC_REBOOT
  typec: define sgm7220 typec func
  fix mass_storage module GKI build issue
  copy mass_storage driver from Kernel as initial version
  typec: improve rt1715 code
  typec: remove qcom pmic code
  arm/dts: improve rt1715 typec and pd compatible
  hall pen kernel bringup
  typec: Add sgm7220 driver initial version
  charging: add bc1.2 feature
  DLKM: force 5V/2A charging
  charging: fixed build error for sgm41542 default code
  charging: add SGM4154x charger basic driver
  pct1812_mmi: self-test and sysfs interfaces
  power: add detect sd77624 ic hw
  goodix_brl_mmi: Add mmi status check.
  Modify the goodix driver code format
  Update the poll mask when polling is aborted by hal
  mmi_sys_temp:fix compilation error.
  power: update sd77624_fg driver
  qti_glink_charger: Add sysfs node for factory wireless testing
  charge:Enable aicl at factory boot mode
  touchu:gc7372 tp modify for charger test and gesture
  usbnet:USBLAN Super speed plus support
  dlkm/wl2866d: disable avdd1 when device shutdown
  backlight: add aw99703 led boost configuration
  charge: define pps charge min out current
  charge: add limit main charge flag
  DLKM: Update SD77426 driver
  Add panel notification for egis rbs driver
  regulator: fix aw37501 gki build error
  regulator: add aw37501 lcd bias power driver
  pd: Add rt1715 driver initial version
  Cypfq: dlkm: Gc7372 add enable/disable wake irq for single tap
  Support sensitivity mode function
  power: introduce sd77426 fuel gauge driver
  power: add rt9426a driver
  add glink oem command to get LPD info
  focaltech_v2_mmi: keep rst high when config only for IDC
  mmi_info: Increase the MAX_BL_BUILD_SIG
  Cypfq: dlkm: Add power off process for ili9882a
  Update goodix berlin code to V1.1.21
  charge: notify mmi-smbcharger-iio while charge pump start working
  charging: Add PD charging in DLKM
  pct1812_mmi: firmware upgrade
  pct1812_mmi: pct1812ff kernel driver
  Cypfq: dlkm: Add charger detection feature for ili9882a
  mmi_annotate: support minidump store
  Support touch corner/edge suppression function.
  Cypfq: dlkm: Support single tap feature for gc7372
  cp: Remove charge rate, age, cycle
  Cypfq: dlkm: Change read vendor id process
  hall driver bring up
  watchdogtest: support build in kernel-5.10
  Cypfq: dlkm: Improve gcore_mmi driver
  Cypfq: dlkm: Add GKI module dir definition
  charging: modify heartbeat cycle to 100s
  remove the mmi_info module dependency
  dlkm: bo: Support single tap feature for ili9882_mmi
  charger: Rename sc8549 to cp
  xpeng: input touch add game api
  charger:adaptive charger tolerance optimization
  goodix_fod_mmi.ko: Update Makefile to compat kernel 5.10
  Use bi_bootmode instead of mmi_bl_bootmode.
  When find drm panel successfully, set panel_status to 0
  FPS: Support multi-fingerprint function.
  put register_panel_notifier into ts_mmi_worker_func.
  Delayed active panel detection
  backlight: fix lm3697 probe fail panic
  backlight: add lm3697 led boost configuration
  use bi_bootmode to query the bootmode
  Cypfg: dlkm: Improve gcore_mmi driver
  fixup! (CR): mmi_info: read all from bootconfig
  mmi_info: read all from bootconfig
  backlight: add lm3697 hbm current mode configuration
  mmi_info: read bootreason from bootconfig
  utags: reads bootdevice name from bootconfig
  qpnp_adaptive_charge: Add adap_reinit
  charge: resolve sc85xx charge icon dont disappear
  cp: improve 8549 read status register
  modules: add leds-indicator-pwm.c driver
  parallel:init thermal parameter at high temp
  Rogue PRC ST54x driver
  Close the calibration operation after fwupdate.
  Modify bm ulog and mmi chargers for Android S
  focaltech_v2_mmi: disable focaltech driver drm notify
  ffc: add ffc featrue
  CP: Remove unuseful code
  qc3p:workaroud qc3p shalcomm adapter
  Modify the touchscreen report rate
  CP: Use CP read battery voltage
  slg5bm43670: Update slg5bm43670 for Kernel 5.10
  QC3P:add afvc compensation voltage
  PD:add afvc compensation voltage
  pstar: input touch update edge set
  pstar: input touch add edge support
  focaltech_v2_mmi: don't set gesture_mode enable as default
  Adjust the heartbeat time to 100s in discharging state
  sx937x:Android S bringup
  Solve the problem of incompatible type 'struct timespec64'
  Slove the build error: redefinition of 'panel_event'
  fpc_fps_mmi: Modify Makefile for Android S build ko
  adjust moto_f_usbnet driver makefile
  Bring up touchscreen_mmi in Rogue S project.
  Bringup stmicro_mmi touch module in kernel-5.10
  Cypfg: dlkm: Improve gcore_mmi driver
  Revert "(CR) sx937x:mv ps_get_state to work callback"
  Revert "(CR) sx937x: change global_sx937x initialization position"
  Modify the ITO test command
  sx937x: change global_sx937x initialization position
  sx937x:mv ps_get_state to work callback
  qpnp-adaptive-charge: use mmi_charger to contrl battery charging
  add vote mechanism support for mmi_charger
  qc3p:limit pulse not beyond max count
  use disable_irq_nosync in interrupt handler
  Add force_charging_enabled sysfs
  Add force_charger_suspend sysfs
  CP: add cp switch enable detect
  CP: force disable CP while plug out usb cable
  CP:Add switch enable detect function
  cp: config sc8549 fault bit
  Fix the build error on denver project.
  add charger state sync interface
  Revert "(CR): send charge rate uevent when power supply changes"
  Need to update fw on resume for zero-flash touch IC.
  novatek: Support parameter upgrade method
  st21nfc:support build in kernel-5.10
  qc3p:Set AICL thres at qc3p pmic charging
  dlkm: add enable CP api
  add power ctrl for fpc driver
  add power ctrl for goodix kernel driver
  aw8695: move camera start entry before the vibrator timer
  dlkm: Touch gc7372 bringup
  pstar: vibrator add moto fw
  Fix artificial vibrating pattern caused AF always on.
  charge: Add thermal interface
  qc3p:clear sm work when vbus off
  Add sense off process to enter the idle process.
  CP: add qc3p charge policy
  CP: Reorganize charge pump
  Print ADSP battery manager log if charging changes
  Create bm_adsp_ulog module
  qc3p:config 33W charging current
  sx937x:fix wrong POWER_SUPPLY_ONLINE define
  Cyprus: dlkm: ili9882 driver improvement
  input: focaltech_v2_mmi: add ft3519 upgrade function
  Solve the build error: MODULE_IMPORT_NS missing
  pstar: sar: support multi parameters for different panels
  aw8695: optimize vibrator&camera resonance issue
  Stmicro: Support parameter upgrade method
  Add flash mode sysfs node in touchscreen mmi
  dlkm: lm3697: free resouces when init failed
  DLKM: bring up corfup PD 30W charging
  charging: pick base parellel charger from mmi_parallel_charger
  send charge rate uevent when power supply changes
  bq25980:update charge VOUT parameter
  bq27426: Fix kbuild to use ANDROID_BUILD_TOP
  mmi_info: support build in kernel-5.10
  mmi_annotate: support build in kernel-5.10
  utags: support build in kernel-5.10
  bos0614_mmi: fix alignment issue
  Enable PANEL_NOTIFICATIONS for berlna touchscreen
  Revert "(CR): fix user version build error"
  Add NVT_SET_TOUCH_STATE to avoid compilation errors
  Deleate fts_system_reset in wait_for_ready
  Add 30W turbo charger type
  touch mmi:declare dependency.
  dlkm: leds_aw99703: free gpio if init failed
  dlkm: leds_lm3697: add initial version lm3697 driver
  Fix berlna user version build error.
  Support novatek single tap in berlna
  fix user version build error
  sx937x:change offset val bit mask
  touch mmi:2nd patch, fix user version build error.
  touch mmi:Fix user version build error.
  charger:mmi_hb_wake lock not released
  SX937x: USE power online state
  bos0614: access to sysfs for others
  bos0614_mmi: waveform shape feature
  focaltech_v2_mmi: bringup ft3519 on kernel 5.4
  qc3p:config ibus max to 3A
  mmi_parallel_charger:reduce charge rate report
  dlkm: Cyprus: touch: ili7807s bringup
  Add Egis fps kernel module for rbs
  Support report rate switch
  Add game mode parameter adjustment function
  input: goodix_v1510: change to api in chip vendor whitelist
  Add pollForEvent check mechanism
  smbcharger:QC3P charging support
  module:QC3P charging support init
  module:mmi parallel charger qc3p repo init
  bos0614_mmi: support rev C
  Delete the calibration process on probe
  Support single tap to wake the device
  power:fet_control: Charge path gpio too slow
  Fix the BTN_TOUCH report error
  Solve the issue that firmware can not be updated.
  Add support for Build_external_kernelmodule.mk
  touch:sleep in atomic context
  input: goodix_v1510: set regulator avdd load to 50mA
  Do not set scan mode when setting charge mode
  use request_threaded_irq to handle TP events.
  focaltech_0flash: rm register_panel_notifier null define
  bos0614_mmi: fix few bugs
  input: focaltech_0flash_mmi: add panel notifications
  Report BTN_TOUCH event when the fingers are lifted.
  fet_control: banks: Enable only pm8350b chrg for usb2/3
  sx937x: reset sar on i2c failures
  update the charger_suspend config in charger configure
  synaptics_mmi_class: fix broken build
  berlNA: support usb detection of novatek TP.
  Get usb status through POWER_SUPPLY_PROP_ONLINE property.
  stmicro: Add USB detection based on touch class
  ETS: Solve the compilation problem of androidR
  Support TP report rate switching
  regulator: wl2866d: support to read init value from dts
  Resolve compilation errors for nio
  Support touchscreen class for novatek
  Move sysfs node to the parent of battery device
  power: fet_control: Update paths on usb psy prop online
  qrng: Add qrng driver
  qti_glink_charger: Add sys node to pass TCMD to ADSP
  qti_glink_charger: Add sys node to set pmic_icl
  Resolve compilation errors with unused parameters "ret"
  Resolve compilation errors with unused parameters
  charge: add sc8549 driver for corfuP product
  Export panel supplier method for ST
  Use supplier to distinguish limit files.
  power:fet_control: Batt flip dischg fet ctl, chg cur_en
  qti_glink_charger: Add sys node to pass TCMD to ADSP
  input: goodix_mmi: add goodix i2c 1510 version driver
  qti_battery_charger_mmi: Initial add
  bq25890_mmi: Fix power supply type
  mmi_charger: Add voltage and current combos
  driver:adjust bq25960 parameter
  synaptics_mmi_class: gpio access func can sleep
  Add calibrate sysfs node for stmicro TP.
  synaptics_mmi_class: rename files
  bq25960:remove online property
  power:fet_control: banks flip batt fet-control driver
  mmi_charger: Add ext_charger
  bq25890: Add get/set for fcc and fv
  Revert "(CR) synaptics_mmi_class: fix build issue"
  bos0614_mmi: added features
  synaptics_mmi_class: fix build issue
  Support fw update at device startup for berlin.
  fixup! (CR) synaptics_mmi_class: banks cli bringup
  synaptics_mmi_class: banks cli bringup
  smb:integrate charge pump ffc feature
  stmicro_mmi: build and config ids
  Remove pressure events
  charger:bq25960 30w charger support
  charger:enable charger rate at 30w charger
  Enable irq during touch resume for berlin.
  pstar: input touch stm fix issue
  fixup! (CR) stmicro_mmi: bug fixes and new features
  stmicro_mmi: bug fixes and new features
  pstar: input touch fix disable irq panic
  Get the profile id from device tree
  Add new platfrom driver to support smb charger
  Add change to be more compatible
  Reorganize files to meet compilation mechanism on enabled GKI products.
  aw8695: Fix compilation error of aw8695 for android R
  Make Trustonic TEE driver module compatible with kernel 5.4.
  mmi_parallel_charger:enable parallel charger at R
  charge: Config charge rate for internal FG product
  Charge: Config step first current comp for CP
  Charge: config min tuning current
  Charge: Clear err flag before start CP
  mmi_parallel_charger: Add Turbo_30w power level
  charge:notify smb5 while charge pump start working
  dlkm: no need rerun aicl for odessa
  dlkm: add enable CP api
  mmi_parallel_charger: set charge_control_limit as writeable
  mmi_parallel_charger: UI support for 45W charger
  fg: soc report 99% lasted for long time
  mmi_pl_chg: Add mmi_cycle_count to calculate batt age
  mmi_parallel_charger: Add factory mode detect
  mmi_pl_chg: clear pmic limit after remove pd power
  mmi_parallel_charger: "battery" psy update PROP_STATUS
  mmi_parallel_charger: optimize 45w charger policy
  optimize the charging logic in thermal mitigation process
  Improve the PD charging performance in high temperature
  mmi_paralel_charger: Enhance the compatibility of PD charger
  mmi_parallel_charger: enhance the stability of sm work
  mmi_pl_chrg: improve the chrg recovery handling
  mmi_chrg_manager: optimize thermal mitigation logic
  mmi_pl_chrg: create some sysfs node for setting mmi_params
  mmi_parallel_charger: optimize temp zone structure
  mmi_parallel_charger: Optimize 40w charging logc, 1.0
  mmi_parallel_charger: Optimize 40w charging policy
  mmi_parallel_charger: Create Heartbeat work
  DLKM: mmi_parallel_charger
  pstar: input touch support cqa test
  bq25890: commit initial version
  bq27426: guard extra batt props
  correct the charger present based on charger type
  pstar: input touch stm bring up
  dlkm: bo: Improve touch driver for novatek
  bos0614_mmi: build kernel code
  bos0614_mmi: initial reference driver
  mmi_charger: Option to start with no factory kill
  sx933x: handle reinitialize during suspend
  pstar: input touch st bring up
  charger:revert parallel charger driver
  Update Trustonic TEE driver module to version 410a-V107
  synaptics_i2c: Compilation errors
  Revert "(CR) synaptics_mmi_class: banks cli bringup"
  sec_mmi: fix regulator get/put
  synaptics_mmi_class: banks cli bringup
  kernel:charger rate duplicate with parallel charger
  kernel:paralle charger driver
  kernel:bq25980 charger driver
  raydium_mmi: fix build error
  uwb: sr100: add gpio-exp dep for banks
  regulator: Add wl2866d  driver.
  ili9882_mmi: CQA test node update for tianma panel
  nio: input touch add edge suppression
  regulator: init all wl2864c register when probe driver
  driver/GoodixFP Add the soft dependency of gpio-pcal6408
  Add register dump interface for WL2864C
  Revert "(CR): Add register dump interface for WL2864C.."
  Adjust the charger configure data
  Adjust charger configure data
  Add register dump interface for WL2864C..
  Fix compile errors for ilitek_0flash_mmi module
  Fix deprecated function call on ilitek_0flash_mmi
  regulator: modify wl2864c iovdd current limit
  fpc1020: fix null pointer panic issue
  exfat:Fix panic of handlings of unhashed alias
  ili9882_mmi: support ili7806s IC
  dlkm: Improve novatek touch process when using GKI
  uwb: sr100: make clocks optional
  uwb: sr100: allow cansleep gpio funcs
  gpio: pcal6408: fix set output
  qpnp-smbcharger-mmi: Add PD charging in DLKM
  dlkm: sn1xx: fix tcmd ioctl error
  DLKM: leds_aw99703: support update by panel config
  sm4350/sensor: optimze sx937x log output
  nio: input touch nova add delay for request fw retry
  dlkm: fix free irq warning for chipone sensor
  Charge: report battery power supply change
  vl53l5: Support 32bit compatible Kernel driver for vl53l5
  dlkm: fpc1020: free gpio when remove
  Correct the config_id and build_id errors of focaltech IC
  gpio: pcal6408: add initial module
  Modify compilation error of synaptics
  Modify compilation error of sec_mmi
  Modify compilation error of stmicro
  qpnp-smbcharger-mmi: Add charge_full_design in battery psy
  mmi_sigprint: Fix CFI check failure
  input: focaltech_v2_mmi: change some focal driver macros
  synaptics_tcm_mmi: mmi status check
  stmicro_mmi: mmi status check
  sec_mmi: mmi status check
  stmicro_mmi: identify product by chip id
  stmicro_mmi: update driver to version 5.2.19
  stmicro_mmi: use with touscreen class driver
  input: touchscreen_mmi: add palm detection sensor class
  input: focaltech_v2_mmi: add palm detection function
  aw869xx: keep using default device name
  dlkm: add qti_glink_charger driver
  dlkm: charger driver for Moto common features
  DLKM: mmi charging LKM must probe after qpnp smb5
  DLKM: use new api in mmi-pmic-voter.c
  Revert "(CR): DLKM: build error on user build"
  module: remove useless code in charging LKM
  input: focaltech_v2_mmi: enable single tap function
  input: focaltech_v2_mmi: add usb charger detection
  input: focaltech_v2_mmi: add sys node to upgrade firmware
  mmi_sigprint: Use a new way to get kallsyms_lookup_name()
  kernel:stop incresing vubs while charger plug out
  usbnet: correct usb_ether_xmit return value
  aw8695: Add start/stop vibrating notification to actuator.
  usbnet: fix CFI checking failure
  module: use orderly shutdown in factory mode
  denver/sensor: update sarsensor params
  module: add address and data api for tcmd
  Chipone: Compatible with android R with 5.4.0
  dlkm: bo: Improve novatek 0flash driver
  FM: fix current leak on GPIO47
  add mmi_relay notify interface by conditional
  module: removed nonstandard props
  qpnp-power-on-mmi: fix build error on user build
  mmi: fix power supply change frequently
  dlkm/qpnp-power-on-mmi: change vfs_write to kernel_write
  ili9882_mmi: support panel gesture config
  ets_fps_mmi: Register panel notifier for display on/off
  input: focaltech_v2_mmi: Configure power supply
  dlkm: bo: Support multi panel-supplier for novatek
  DLKM/Sar:add abov 7ch driver
  input: focaltech_v2_mmi: add V2 version of i2c focal driver
  Register mmi_relay notification for touchscreen
  Register mmi_relay notification for FOD
  An common module used for all of module communication.
  Revert "(CR): An common module used for all of module communication."
  Revert "(CR): Register mmi_relay notification for FOD"
  Revert "(CR): Register mmi_relay notification for touchscreen"
  mmi_relay: Add gki flag to avoid module load failure
  driver/sar-sensor: sx937x bring up
  Register mmi_relay notification for touchscreen
  Register mmi_relay notification for FOD
  An common module used for all of module communication.
  ili9882_mmi: deep sleep update with gesture config
  nova_0flash_mmi: improve panel-supplier
  ili9882_mmi: tp suspend improve
  dlkm: bo: Support multi panel-supplier for novatek on 5.4.0
  UWB bring up for driver
  ili9882_mmi: improve RESUME_BY_DDI support
  nova_0flash_mmi: add LCM_FAST_LIGHTUP support
  Support tapTowake feature on android R for novatek
  chipone_tddi_mmi: ICNL9911C and ESD improve
  exfat: fix build err on user build.
  chipone_tddi_mmi: support ICNL9911C
  NFC driver mmi and DLKM support
  driver/sar-sensor: build error on user build
  driver/sar-sensor: sx937x bring up
  Continue to probe touch even if no matching panel is found
  DLKM: build error on user build
  [capsensor] add debug support for cap sensor
  Add 5ms delay for wl2864c ramp-up
  driver/sar-sensor: build error on user build
  dlkm: bo: Fixed compile error on android R
  [capsensor] add debug info for read_rawdata
  Ibiza: Touch ICNL9911C bringup
  Revert "(CR): utags.ko: solve the unused remove_proc_subtree symbol definition"
  Revert "(CR): utags.ko: force this module unloadable"
  DLKM: disable update_now and usb_otg property
  DLKM: bring up charging module
  DLKM: submit the charging base code
  watchdogtest: add qcom_wdt_trigger_bite for trigger wdog
  exfat: workround end_buffer_async_write undefine issue in GKI 5.4
  guamp: input touch fix esd lock issue
  dlkm: chipone: fix build error on kernel 5.4
  driver/sar-sensor: add sx937x base driver
  hall: stylus bu520xx: Fix wakeup_source event API change
  touch:nt36xxx: add ESD config
  hall: stylus bu520xx: fix the compilation errors
  mmi_info: export API for dynamically match mmi device
  exfat:fixed exfat compliling failed on kernel 5.4
  touch:nt36xxx: NT36525c improvement
  touch:nt36xxx: support panel ic info
  Force Trustonic TEE schedule run in Silver cores.
  ili9882_mmi: capri txd/tm ili9882 touch bringup
  Revert "(CR) ili9882_mmi: capri txd/tm ili9882 touch bringup"
  ili9882_mmi: capri txd/tm ili9882 touch bringup
  denver: modify wl2864c driver
  touch:nt36xxx: support nt36525c
  touch:nt36xxx:support panel config
  dlkm: bo: Bringup touch driver for novatek
  Modify the compilation error of the device_create function
  Use alloc_chrdev_region instead of input_get_new_minor
  the battery should be charged in taper-charger until iterm
  dlkm: bo: Bringup touch driver for novatek
  sx933x: Add a dummy flush function
  sx9338: sar sensor bring up
  Capri:Add compability of chipone driver
  Capri:Add support for Chipone FPS
  input: ilitek_0flash_mmi: change memcpy wlen value
  Fixing factory kill function
  Flip sign of current during taper check
  Add battery profile v5
  Print all Ra tables
  watchdogtest: fix kallsyms_lookup_name issue in GKI 5.4
  touchscreen_mmi: import correct Modules.symvers in GKI build mode
  tzlog_dump: use ioremap to map ram from kernel 5.4
  tzlog_dump: workaround msm_dump_data_register undefine issue in GKI 5.4
  Add battery number to logging
  fix ITPOR and CFGUPMODE logging
  sec_mmi: import ANDROID_GKI_VFS_EXPORT_ONLY namespace for kernel_read
  utags: import ANDROID_GKI_VFS_EXPORT_ONLY namespace for kernel_read
  Refactoring code to use new wake API
  himax_0flash: fix driver resume issue
  focaltech_0flash_mmi: add reg log when tp not in normal mode
  guam: input touch ilitek set deep sleep mode
  Add Trustonic TEE driver module
  input: ilitek_0flash_mmi: add ic_ver sys node for MotoCare
  mmi_info: import correct Modules.symvers in GKI build mode
  guam: input touch ilitek enable gesture
  ili9882_mmi: fix build breakage on R
  guam: input touch add ic_ver support
  ets_fps: filter unexpected double-click event
  mmi_info: add dependency of mmi_annotate module
  qpnp-smbcharger-mmi:  Charger Temp change adjust
  utag: free the buffer when failed to open utag
  utag: report the return value of store_utags() to userspace
  guam: input update touch ilitek fw 02
  Turn off the output log of the LSI touch FW raw data.
  modify the compilation errors of goodix_berlin
  sec_mmi: Don't USE_STUBS
  restrict compilation for snd_soc_tfa9874
  fixup! (CR): Instead of vfs_write with kernel_write
  Instead of vfs_read with kernel_read
  Revert "(CR): Instead of vfs_read with kernel_read"
  Instead of vfs_read with kernel_read
  Instead of vfs_write with kernel_write
  synaptics prob if it matches the value of "mmi,panel_name"
  Specify the dependency of the synaptics touchscreen modules.
  Modify synaptics compile error on Viking.
  Add CONFIG_DRM_PANEL_PANEL_NOTIFICATIONS on Viking
  Determine whether to load by "mmi,panel_name"
  touch: nt36xxx: Resolve include build error
  Awinic: aw8624: Replace wakeup_source API with macro
  touch: nt36xxx: Replace wakeup_source API with macro
  pmic: Replace wakeup_source API with macro
  mmi_wake_lock: Resolve malloc build error
  Awinic: aw8695: Replace wakeup_source API with macro
  Awinic: aw8697x: Replace wakeup_source API with macro
  Awinic: aw8697: Replace wakeup_source API with macro
  touch: synaptics: Replace wakeup_source API with macro
  touch: focaltech: Replace wakeup_source API with macro
  touch: sec_ts: Replace wakeup_source API with macro
  touch: ilitek: Replace wakeup_source API with macro
  touch: fts: Replace wakeup_source API with macro
  misc: ets_fps: Replace wakeup_source API with macro
  touch: synapticsi_dsx: Fix wakeup_source event API change
  wakeup_source: Choose wakeup_source API based on version
  aw8624: Fix build error due to wakeup API changes
  nt36xxx: Fix build error due to wakeup API changes
  Specify the dependency of sec_mmi on touchscreen_mmi
  Slove the compile error of of_drm_find_panel function
  himax_v2_mmi: fix build failure
  Modify samsung compilation errors for viking.
  Temporarily modify the value of device minor
  Add mmi_bl_bootmode function to get boot mode.
  Add DRM PANEL notifications to touchscreen
  tas2562: fix build failure
  input: ilitek_0flash_mmi: add usb charger detection
  Modify touchscreen class compilation errors on Viking.
  vl53l5: add stmvl53l5 init and deinit functions
  vl53l5: initial version of vl53l5 sensor driver
  usbnet: fix GKI compliance issue on sock access
  dubai: bring up goodix fingerprint for android R
  guam: input add touch ili9882n factory support
  guam: input add touch ili9882n support
  DLKM/Sar:remove CONFIG_CAPSENSE_CONTROL_VDD for borneo NA
  DLKM:modify capsensor_enable_flag_t enum for abov
  aw869xx: Fix build issue on Viking
  aw869xx: Add awinic 869xx haptic driver
  regulator: modify slg5bm43670 driver to support kernel 5.4
  fir: update for coefficient read
  cci: modify cci_intf driver to support kernel 5.4
  DLKM:control VDD off when sar sensor is off
  Modify the compilation error of ST touchscreen on Viking
  guamna: input touch himax runin debug
  aw8697: play weak haptic on implicit setup
  aw8697: improve latency
  synaptics_mmi_class: remove handler fix
  fts: support touchscreen class
  Sx933x:use ANDROID_BUILD_TOP in Kbuild
  utags.ko: force this module unloadable
  Revert "(CR): qpnp-smbchrgr-mmi: Optimize charger current over Temp"
  fg: Add Golden Profiles v4
  qpnp-smbchrgr-mmi: Optimize charger current over Temp
  DLKM: himax_v2_mmi: fix compile error
  AndroidKernelModule.mk: use Qcom dlkm file for GKI products
  sec_mmi: add clip area
  guamna: input fix touch himax allocate memory
  Revert "(CR): AndroidKernelModule.mk: sync the file with qcom dlkm"
  AndroidKernelModule.mk: sync the file with qcom dlkm
  power: Fix build error due to wakeup API changes
  aw8695: Fix build error due to wakeup API changes
  ets_fps: Fix build error due to wakeup API changes
  focaltech_0_flash: Fix build error due to wakeup API changes
  focaltech_0_flash: Fix build error due to wakeup API changes
  ets_fps: Fix build error due to wakeup API changes
  aw8695: Fix build error due to wakeup API changes
  power: Fix build error due to wakeup API changes
  Initial goodix_berlin touchscreen driver code
  touchscreen_mmi: fix undefined symbol error on kernel 5.4
  mmi_sys_temp: use ANDROID_BUILD_TOP in Kbuild
  exfat:fixed fs_error access null pointer
  ikswq-123771: power: scale down initial chrg curr
  sensor_class: use ANDROID_BUILD_TOP in Kbuild
  mmi_info: use ANDROID_BUILD_TOP in Kbuild
  mmi_annotate: vmap the physical ram in kernel 5.4
  utags.ko: solve the unused remove_proc_subtree symbol definition
  Egis.FPS Call early panel power-on when finger detected
  guamp: input add touch ilitek support
  nt36xx: Fix bld error due to wakeup_api_changes
  focaltech_0_flash: fix bld error due to wakeup API changes
  touch: nt36xxx: fix build error due to wakeup API changes
  ets_fps: fix build error due to changes in the wakeup API
  fix ST NFC driver compile error
  power: Charge curr over adjustment- check both batts
  power: Schedule 1sec smbchrg heartbeat when in ocp
  guam: input update init drm callback timer
  guam: input add touch ilitek support
  synaptics_mmi_class: postpone loading functions
  focaltech_0flash_mmi: ft fw file not found
  focaltech_0flash_mmi:8006s_aa update irq config
  input: touchscreen: Check buffer size
  input: touchscreen: focaltech: fix pointer comparison
  Kiev: Add usb detection based on touchscreen class
  Initialize usb state when registering the usb notification
  sx933x: reinitialize via i2c watchdog
  Add SOH function
  qpnp-smbcharger-mmi: Offer hvdcp_power_max setting by sysfs
  sx933x: reset and re-init the device on i2c failures
  power: Remove FG reset for factory bootmode
  focaltech_0flash_mmi:8006s_aa enable irq when supspend
  power: Remove FG reset check for factory sw build
  Add macro definition to control unused code in Kiev
  Kiev: ft8756:report touch events to touchscreen class
  Double tap optimization
  focaltech_0flash_mmi: fix tp recovery issue for fts8006s_aa
  power: TI FG dm_ver3
  aw8695:Ensure wakeup_source API is compatible for all kernels
  aw8697: optimize sequencer registers write
  aw8697: i2c transaction logging
  aw8697: treat 100ms haptics as short vibration
  fts: initial STmicro touch driver code
  fixup! (CR): touch:Ensure wakeup_source API is compatible for all kernels
  awinic:Ensure wakeup_source API is compatible for all kernels
  touch:Ensure wakeup_source API is compatible for all kernels
  pmic: Ensure wakeup_source API is compatible for all kernels
  touch: Fix sec_ts build error due to wakeup_source API
  Awinic: fix build error due to wakeup_source API's
  touch: synaptics: Fix wakeup_source event API change
  Power: fix implicit bld errs of wakeup_source API's
  focaltech_0flash_mmi: Add ESD log config
  power: smbcharger: Heartbeat Delay if FG reset incomplete
  vl53l1: Make do_gettimeofday compatible to new Kernel version
  usbnet: Use KERNEL_DS to replace get_ds() for compilation
  Kiev: Add ft8756 single-tap wakeup gesture
  Revert "(CR): Send the touchscreen status to display"
  Send the touchscreen status to display
  ITERM80 mA
  DLKM: add ktd3136 backlight
  Add sys node ic_ver and name
  DLKM: ilitek_0flash_mmi: add ilitek driver patch for dma
  input: ilitek_0flash_mmi: pull down reset when shutdown
  focaltech_0flash_mmi: CONFIG_DRM_PANEL support
  mmi_info: fix undefined symbol error on kernel 5.4
  mmi_info: remove unexported symbol in kernel5.4
  Kiev: Add touchscreen class for focaltech
  fir: update the power manage logic
  aw8624: Upgrade driver from 1.0.7 to 1.3.9
  Change double tap interval for Egis FPS
  input: ilitek_0flash_mmi: reset IC if esd recovery fail
  Increment dm_ver
  Fixup the definition of unregister_panel_notifier
  focaltech_0flash_mmi: enable ESD for ft8006s_aa
  KeivJP: bringup chipone fps driver
  Added dts901b1 thermopile kernel module
  input: ilitek_0flash_mmi: import some ili common changes
  DLKM: leds_aw99703: change backlight type to Platform
  Use kernel_definitions.mk if AndroidKernel.mk not existed
  input: ilitek_0flash_mmi: resume touch by ddi
  Add Golden Profiles from testing
  input: ilitek_0flash_mmi: add support for single tap
  qpnp-smbcharger-mmi: update ocp check
  synaptics_mmi_class: handle delayed init
  DLKM/sar:optimise process of sar to support 4 channels
  aw8695: nairo: add 3 ATT ringtone waveforms
  himax_v2_mmi: Add productinfo & buildid sys node
  focaltech_0flash_mmi: support FT tp ft8006s_aa
  Guam: input update touch chipone resume time
  synaptics_mmi_class: panel ready handling
  aw8697: Add "Moto Retro" to ringtone map
  fg: Trigger POR in factory mode or factory software
  DLKM: leds_aw99703: don't pull down enable pin again
  racer 5G: aw8695: Add 3 new ringtone waveforms
  aw8697: A fix of rtp playing from Awinic
  aw8697: Stop repeatedly waveform playing
  aw8697: Support repeatedly waveform playing
  adjust factory image identification
  guam: input add moto care support
  aw8695: A fix of rtp playing from Awinic
  aw8695: Cancel the 120s timer when stopping vibrating
  aw8695: Stop repeatedly waveform playing
  guam: input touch add class ic_ver support
  dlkm: Add himax touch info node for Moto Care
  Revert "(CR): modules: power: Add Adaptive Charging feature"
  Revert "(CR): qpnp-smbcharger-mmi: Fix getting stuck in adaptive mode"
  mmi_sys_temp: Add thermal sensors monitor work
  dlkm: Add novatek touch info node for Moto Care
  Add the judgment on gesture_enabled configuration in dtbo
  Double tap optimization
  aw8697: smith: Add boot-up vibration
  synaptics_mmi_class: fix suspend/resume callbacks
  Release touch events after suspend
  Set the type of release event to the correct value.
  dlkm: Improve himax_0flash_mmi for single tap
  add dm_code ver 1 to both fg
  p938x_charger: wake lock initialized too late
  capsense: Add dummy set_poll_delay function
  touchscreen_mmi: optimize poison event
  synaptics_tcm_v2: Add touchscreen class handle touch event
  touchscreen_mmi: Add poison slot support.
  guam: input update touch chipone driver version
  dlkm: bo: Clean up focaltech_0flash_8756_mmi
  ilitek_0flash_mmi: add ili9881x initial driver
  backlight: Add wleds driver aw99703
  Nairobi hx83112f touch driver improvement
  ASoC: aw882xx: set speaker default impedance value
  aw8695: Support repeatedly waveform playing
  Set the default value of gs_distance is 0x1e
  Syna: Modify the baseline update process
  touchscreen_mmi: do not lock in irq context
  [Kiev]To support USB calibration
  sx933x: update flip registers after init
  sx933x: turn off sensing before flip updates
  Add qpnp_adaptive_charge module
  touchscreen_mmi: Properly handle the deletion process.
  Add more configuration commands for FG
  guam: input update touch himax driver
  sec_mmi: show firmware version info
  touchscreen_mmi: Control baseline update by FPS notifier
  sec_mmi: Add update baseline method for lsi IC
  touchscreen_mmi: Add update baseline entry
  goodix_fod_mmi: Add device enable notifier chain
  aw8697: Disable external triggers
  touchscreen_mmi: selete on state when panel register.
  Synaptics: Add hold-distance function, command is 0xdc.
  synaptics: Add suppression control function
  Report finger lift events after touch suspend.
  synaptics_tcm_v2: Add wait_for_ready methord
  touchscreen_mmi: Add default pinctrl methord
  Solve NFC wake lock issue
  touchscreen_mmi: exports touch events handler
  Syna: Report down/up touch events to touchscreen class
  touchscreen_mmi: qcom drm notifications support
  sec_mmi: fix compile error
  add panel_notifier.h to mmi kernel modules area
  Syna: Modify the ratio of the major and minor values
  guam: input update touch himax driver
  input: himax_v2_mmi: Sync touch suspend and resume thread
  input: himax_v2_mmi: set hx83012d only if defined
  Set up common vendor id for fps
  fg: Trigger POR through sysfs instead of factory mode
  capsense sx933x recive headset state
  ASoC: implement ICN control in TAS2562
  Fail out of probe gracefully
  smbcharger-mmi: Report turbo charging for wireless
  p938x_charger: report fast charging if >= 15W
  Revert "(CR): touchscreen_mmi: Do not write if value is same with cached"
  Fix complilation error after removing -fno-builtin
  qpnp-power-on-mmi: support hw_warmreset feature
  fix 'rmmod ets_fps_mmi' error
  Use touch class unified interface to support edge gesture
  touchscreen_mmi: Do not write if value is same with cached
  touchscreen_mmi: use try to call and try to read
  touchscreen_mmi: update RW sys entry.
  touchscreen_mmi: Only update refresh & charger when active
  touchscreen_mmi: Add macro to get touch status
  touchscreen_mmi: Add edge touch support
  Revert "(CR) qpnp-power-on-mmi: support hw_warmreset feature" it will cause compile error on other mainline
  qpnp-power-on-mmi: support hw_warmreset feature
  touchscreen_mmi: Correctly Initialize temp cmd variable
  sx933x: update registers on open/close
  Reset fuel gauge in factory mode
  Modify synaptics' log information
  Add power flag to indicate power status.
  dlkm: Himax 0Flash touch improvement
  dlkm: Himax 0Flash touch improvement
  dlkm: Himax 0Flash touch improvement
  dlkm: Himax 0Flash touch improvement
  ASoC: add aw882xx volume ramp function
  exfat: use kvzalloc instead kzalloc in exfat_fill_super().
  power: bq_27426_mmi: Add FG OCV voltage support
  Syna: USB detection support
  dlkm/sensor: optimize sx933x boot-time
  Modify the value of synaptics major and minor
  Solve resume and suspend confusion,causes touchscreen crash
  Revert "(CR): exfat: use kvzalloc instead kzalloc in exfat_fill_super()."
  exfat: use kvzalloc instead kzalloc in exfat_fill_super().
  DLKM/sarsensor:add tcmd test interfaces for abov mmi
  fixup! (CR): aw8697: Add error checking to RTP mode
  fixup! (CR): goodix_gtx8_v1430_mmi: Port goodix v1.4.3.0 driver code
  fixup! (CR): create mmi_info module
  Pass absolute path of "TOP" to Kbuild.
  ASoC: aw882xx:correct a property variate in parse dt
  dlkm: Himax: Apply for new interface for single tap
  himax: Compatible with the way of gesture to report key
  ASoC: aw882xx driver update
  support pill region and gs-distance support
  dlkm: Improve himax_0flash_mmi
  dlkm: Improve himax_0flash_mmi - continous
  dlkm: Improve himax_0flash_mmi
  motorola:qpnp-smbcharger-mmi Clean up commits
  motorola:qpnp-smbcharger-mmi Delay first boot heartbeat
  motorola:qpnp-smbcharger-mmi delay first heartbeat 10 seconds
  dlkm:  himax touch driver improvement
  sx933x: Add generic usb/flip recal options
  dlkm: Support the touch hx83112f for nairo
  watchdogtest: replace msm_trigger_wdog_bite()
  Add synaptics wakeup gesture handler
  Syna: Modify the suspend and resume process.
  Modify the disable-irq process in touchscreen class.
  Allow for multiple batteries and multiple battery profiles
  ASoC: monitor key registers in aw882xx
  DLKM/sarsensor:optimise code for sar sensor of abov
  guam: input add touch chipone fw
  sec_mmi: mutual capacitance range interface
  update CCI tools
  nairobi/sensor: set CONFIG_CAPSENSE_USB_CAL
  dlkm: ets_fps: Set up double tap feature
  guam: add touch chipone support
  Re-Enable DRM Event Notification
  Solve the problem of charge mode setting when power off
  racer 5G: aw8695: Add new ringtone waveform
  dlkm: st21nfc: update driver
  DLKM/sar: Add fw mode detection for A96T346HW.
  mmi_sigprint: add death signal debug feature driver
  DLKM/sar: Add fw mode detection for A96T346HW.
  fixup! (CR) sec_mmi: support holding grip detection
  SDM710: Reduce the spam charger related logs
  Fixup! Check the value to be written
  Check the value to be written
  Add synaptics major and minor
  sec_mmi: support holding grip detection
  touchscreen_mmi: export class kobject
  qpnp-smbcharger-mmi: Fix getting stuck in adaptive mode
  sec_mmi: add grip supp distance register
  sec_mmi: Fix default prop reset on resume
  guam: add touch module support
  fixup! (CR): Solve the problem of I2C error when power off
  Solve the problem of I2C error when power off
  fps: fixup kernel panic by calling input_free_device
  FM: support elna module
  input: himax_v2_mmi: change resume timing for hx83102d
  sec_mmi: add hold distance register
  dlkm: aw8695: fix enter standby mode failture
  Revert:(CR):timeout to wait for wake lock at suspend
  Support dts property to configure force device on option
  modules: power: Update README document for bq27426 driver
  modules: power: Build with battery profile header files
  modules: power: Add battery profile files from TI
  Revert "(CR) aw8697: boost ram loop mode playback"
  Disable navigation in driver
  sec_mmi: timeout to wait for wake lock at suspend
  Take a long time to enter pre_suspended
  modules: power: Add a long heartbeat delay for discharging
  dlkm: Nairobi single tap bringup for TDDI
  modules: power: Add Adaptive Charging feature
  modules: power: Review SMBMMI log messages
  dlkm: support 20W charger
  ASoC: upgrade Aw882xx driver to v0.1.8
  qpnp-smbcharger-mmi: Add POWER_SUPPLY_PROP_HOT_TEMP prop
  p938x_charger: Reduce dc current if battery too hot
  Modify the output format of the testing node
  Update synaptics testiong code to V2.1
  DLKM/sar:modify enable process of abov sar
  DLKM/sar:optimise driver of Abov 5 channels
  Racer_turbo: aw8697: Add new entry in ringtone array
  sec_mmi: cleanup device tree parse logs
  sec_mmi: add pill region support
  Solve the power on problem of Synaptics touchscreen
  Add touch suspend_noirq process
  sec_mmi: report major and minor value in pixel unit.
  Solve I2C error in the first suspend
  p938x_charger: Force dcin-en on when attached
  charge:real time check weak charge
  aw8697: Add error checking to RTP mode
  touchscreen_mmi: Modify the process of panel off and on
  sec_mmi: add delay if power supply off
  p938x_charger: protect fw flash and handle reboot case
  touchscreen_mmi: secondary touch panel supplier
  synaptics_mmi_class: fix firmware file name
  Add Build Flags for Data Width Change
  Add Data Width to CCI_intf
  p938x_charger: Wait for LDO to report charging
  p938x_charger: Update detection algorithm
  fixup! (CR) sec_mmi: update Kbuild
  synaptics_mmi_class: start using touchscreen_mmi class
  touchscreen_mmi: match panel notifier with control dsi
  synaptics_mmi: add touchscreen_mmi class check
  sec_mmi: update Kbuild
  sec_mmi: do not calibrate on firmware update
  aw8697: Move RTP update work out of irq handler
  p938x_charger: remove unneeded resets
  p938x_charger: Remove thermal message print
  add sar svdd regulator in kernel driver
  Change dt-gesture sensor to SPECIAL_REPORTING mode
  p938x_charger: Update tx mode fault handling
  modules: power: Updating chg-iterm spec on documentation
  sec_mmi: Get finger size from dts
  sec_mmi: grip suppression control interface
  p938x_charger: Update rx_connected with tx mode work
  dlkm: ets_fps: fix build issue on kernel 4.19
  p938x_charger: change tm mode address
  p938x_charger: change the length of memcpy in fod_store
  MMI: Add DC psy notifier
  modules: power: Adding documentation to SMBMMI driver
  modules: power: Change Heartbeat delay for fast mitigation
  DLKM: Nairobi NT36672c touch bringup
  Update driver version to 2.0.4
  syna_tcm_mmi_v2: Fix build ID and config ID
  Exfat:fix compile fail on kernel-4.19
  aw8697: boost ram loop mode playback
  input: himax_v2_mmi: change factory self test codes
  regulator: modify slg5bm43670 to support interrupt
  sec_mmi: Fix zero tap gesture coordinate.
  ft8756: Compatible with the way of gesture to report key
  novatek: Compatible with the way of gesture to report key
  input: himax_v2_mmi: add hx83102d driver
  ASoC: add Awinic aw882xx driver
  regulator: Add wl2864c driver.
  kernel/sar: update a96t driver to support two channel
  Register touchscreen class of synaptics
  input: focaltech_0flash: enable FTS_POINT_REPORT_CHECK_EN
  racer: fix cci tools can not access cci-device-1
  Racer5G: system crash when Synaptics shut dowm
  motorola: drivers: Add logic to PD charger detection
  regulator: separated slg51000 driver
  drivers: slg51000: Prevent Driver from touching voltages
  sec_mmi: single/zero tap report format change
  [racer]sx933x: add usb recal for cap sense
  modules:audio:tas2562:Turn off debug macro
  touchscreen_mmi: create more sys entries
  goodix_gtx8_v1430_mmi: Use i2c device to register ts_mmi
  Set samsung touch screen name
  Create the methods to get touchscreen class name
  aw8697: Add moto ringtone array
  touchscreen_mmi: use primary and secondary
  touchscreen_mmi: usb detection
  sec_mmi: touchscreen class based  usb detection
  touchscreen_mmi: generic gesture handling
  sec_mmi: enhance class support
  p938x_charger: add thermal cooling device
  p938x_charger: add notify calls for hal
  aw8697: Use RAM mode for long vibration
  drivers: aw8697: fix kernel panic on RTP
  fixup! (CR): fix unused function in user build
  Sec_mmi firmware update
  goodix_gtx8_v1430_mmi: register tp_mmi notification.
  sec_mmi: add notification function
  sec_mmi: Porting gesture handler to sec_mmi_class.
  touchscreen_mmi: add notification function
  p938x_charger: read current limits from dt
  fixup! (CR): drivers: max170xx: fix reset on factory image
  drivers: max170xx: reset on factory image
  Show Panel supplier
  sec_mmi: register a weak function to fix symbol issue
  p938x_charger: Indicate online based on VRECT
  sec_mmi: register moto touch class.
  touchscreen_mmi: create extra sys entry group.
  Synaptics updated driver code to V2
  fixup! (CR) sec_mmi: fix format conversion
  fixup! (CR) sec_mmi: fix format conversion
  sec_mmi: fix format conversion
  fixup! (CR) sec_mmi: wakeup gestures support
  fixup! (CR) sec_mmi: wakeup gestures support
  sec_mmi: wakeup gestures support
  p938x_charger: program fod and q factor
  Synaptics updated driver code to V2
  goodix_gtx8_v1430_mmi: update fw in moto touch script.
  goodix_gtx8_v1430_mmi: register moto touch class.
  touchscreen_mmi: Add touchscreen class for moto product.
  goodix_gtx8_v1430_mmi: support touchscreen_mmi.
  Revert "(CR): ASoC: add APIs to access digital volume in tas25xx algo"
  drivers: sx933x: add usb recal for cap sense
  sec_mmi: make dynamic refresh rate optional
  sec_mmi: force calibration on power up
  sec_mmi: refresh rate update support
  p938x_charger: read epp capabilities
  ASoC: tas2562 digital volume
  regulator: slg51000: Set SLG IRQ to trigger low.
  Revert "(CR): MMI: Init batt health to GOOD"
  aw8697: support moto vibrator feature
  aw8697: decouple haptics and calibration wave forms
  p938x_charger: Only turn off from usb when charger attached
  fixup! (CR) sec_mmi: remove MMI_STOPSHIP
  synaptics_mmi: fix clang errors
  sec_mmi: remove MMI_STOPSHIP
  ASoC: tas2558: add digital volume control
  ...

 Conflicts:
	Documentation/devicetree/bindings~HEAD
	drivers/misc/Makefile
	drivers/regulator/wl2868c/Makefile
	drivers/regulator/wl2868c/wl2868c-regulator.c
	drivers/regulator/wl2868c/wl2868c-regulator.h
	drivers/sensors/Makefile
	fs/exfat/Kconfig
	fs/exfat/Makefile
	fs/exfat/README.md
	include/linux/input/synaptics_tcm.h
	include/linux/panel_notifier.h
	sound/soc/codecs/Makefile

Change-Id: Ida4ebbd91a32e29c78afe924d26e7ced3a6a42ed
This commit is contained in:
Michael Bestas 2023-04-16 05:32:20 +03:00
commit 8208bde4b8
No known key found for this signature in database
GPG key ID: CC95044519BE6669
1666 changed files with 1016800 additions and 0 deletions

189
AndroidKernelModule.mk Normal file
View file

@ -0,0 +1,189 @@
ifeq ($(call is-board-platform-in-list,taro kalama), 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....)

View 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;
};

View 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>;
};

View 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";
};

View 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>;
};

View 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 >;
};

View 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>;
};

View 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>;
};

View file

@ -0,0 +1,84 @@
* Dialog Semiconductor SLG51000 Voltage Regulator
Required properties:
- compatible : Should be "dlg,slg51000" for SLG51000
- reg : Specifies the I2C slave address.
Optional properties:
- interrupt-parent : Specifies the reference to the interrupt controller.
- interrupts : IRQ line information.
- dlg,cs-gpios : Specify a valid GPIO for chip select
Sub-nodes:
- regulators : This node defines the settings for the regulators.
The content of the sub-node is defined by the standard binding
for regulators; see regulator.txt.
The SLG51000 regulators are bound using their names listed below:
ldo1
ldo2
ldo3
ldo4
ldo5
ldo6
ldo7
Optional properties for regulators:
- enable-gpios : Specify a valid GPIO for platform control of the regualtor.
Example:
pmic: slg51000@75 {
compatible = "dlg,slg51000";
reg = <0x75>;
interrupt-parent = <&gpio6>;
interrupts = <11 IRQ_TYPE_LEVEL_HIGH>;
regulators {
ldo1 {
regulator-name = "ldo1";
regulator-min-microvolt = <2400000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
};
ldo2 {
regulator-name = "ldo2";
regulator-min-microvolt = <2400000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
enable-gpios = <&gpio6 14 GPIO_ACTIVE_HIGH>
};
ldo3 {
regulator-name = "ldo3";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3750000>;
};
ldo4 {
regulator-name = "ldo4";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3750000>;
};
ldo5 {
regulator-name = "ldo5";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1200000>;
};
ldo6 {
regulator-name = "ldo6";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1200000>;
};
ldo7 {
regulator-name = "ldo7";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3750000>;
regulator-boot-on;
};
};
};

View 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>;
};

View 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

View 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

View 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

View file

@ -0,0 +1,809 @@
/*
* 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"
#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, &reg_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 (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);
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]&REG_RD_ACCESS))
continue;
aw99703_i2c_read(aw99703->client, i, &reg_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");

View file

@ -0,0 +1,257 @@
#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
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 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

View 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

View 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

View 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

View 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");

View 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

View 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

View 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

View 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

View 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(&regmap_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, &regmap_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");

View file

@ -0,0 +1,250 @@
/*
* 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_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

View file

@ -0,0 +1,955 @@
/*
* 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;
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");

View 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);

View 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

View 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

View file

@ -0,0 +1,5 @@
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
obj-m += sm5350_bl.o

View 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");

View 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
View 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

View 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

File diff suppressed because it is too large Load diff

10
drivers/fm/Android.mk Normal file
View 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
View 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
View 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
View 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");

View 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
View 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

View 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");

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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_ */

View 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_ */

View 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

File diff suppressed because it is too large Load diff

View 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_ */

View 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);

View 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 */

View 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_ */

View 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;
}

View 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_ */

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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);
}

View 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_ */

View 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");

View 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_ */

View 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__ */

View 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_ */

View 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_ */

View 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_ */
/** @} */

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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);
}

View 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_ */

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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);

View 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;
}

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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

View 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_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
/* 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_FE_H_
#define _MC_XEN_FE_H_
#include <linux/version.h>
#include "main.h"
#include "client.h"
#include "iwp.h"
#include "mcp.h"
#ifdef TRUSTONIC_XEN_DOMU
/* MC protocol interface */
int xen_mc_get_version(struct mc_version_info *version_info);
int xen_mc_open_session(struct mcp_session *session,
struct mcp_open_info *info);
int xen_mc_close_session(struct mcp_session *session);
int xen_mc_map(u32 session_id, struct tee_mmu *mmu, u32 *sva);
int xen_mc_unmap(u32 session_id, const struct mcp_buffer_map *map);
int xen_mc_notify(struct mcp_session *session);
int xen_mc_wait(struct mcp_session *session, s32 timeout, bool silent_expiry);
int xen_mc_get_err(struct mcp_session *session, s32 *err);
/* GP protocol interface */
int xen_gp_register_shared_mem(struct tee_mmu *mmu, u32 *sva,
struct gp_return *gp_ret);
int xen_gp_release_shared_mem(struct mcp_buffer_map *map);
int xen_gp_open_session(struct iwp_session *session,
const struct mc_uuid_t *uuid,
const struct iwp_buffer_map *maps,
struct interworld_session *iws,
struct interworld_session *op_iws,
struct gp_return *gp_ret);
int xen_gp_close_session(struct iwp_session *session);
int xen_gp_invoke_command(struct iwp_session *session,
const struct iwp_buffer_map *maps,
struct interworld_session *iws,
struct gp_return *gp_ret);
int xen_gp_request_cancellation(u64 slot);
int xen_fe_init(int (*probe)(void), int (*start)(void));
void xen_fe_exit(void);
#else
static inline int xen_fe_init(int (*probe)(void), int (*start)(void))
{
return 0;
}
static inline void xen_fe_exit(void)
{
}
#endif
#endif /* _MC_XEN_FE_H_ */

8
drivers/gud/README Normal file
View file

@ -0,0 +1,8 @@
Trustonic TEE is an operating system being shipped with TZBSP
on MSM chipsets. t-base consists of several components in
the secure world(TrustZone) and non-secure world(linux
kernel, Android user space). The t-base driver
communicates with the Trustonic TEE kernel that exists in
TrustZone.

View file

@ -0,0 +1,10 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bos0614_mmi.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -0,0 +1,14 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \
-I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/bos0614_mmi \
-I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/bos0614_mmi/libs/dk-core/src \
-I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/bos0614_mmi/libs/dk-core
EXTRA_CFLAGS += -DMOTOROLA
EXTRA_CFLAGS += -DLITTLE_ENDIAN=1234 -DBYTE_ORDER=LITTLE_ENDIAN
obj-m := bos0614_mmi.o
bos0614_mmi-objs := i2cLinux.o bosDriver.o
bos0614_mmi-objs += libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.o
bos0614_mmi-objs += libs/dk-core/contribs/comm-stack-dk/data.o

View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View 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

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