rM2 all changes
Port all changes from the rM2 zero-sugar branch (https://github.com/reMarkable/linux/tree/zero-sugar) to the 5.4 kernel. Signed-off-by: Alistair Francis <alistair@alistair23.me>5.4-rM2-2.2.x-imx-squashed
parent
739c2edfa2
commit
2abfd510f9
|
@ -679,7 +679,8 @@ dtb-$(CONFIG_SOC_IMX7D) += \
|
|||
imx7d-zii-rpu2.dtb \
|
||||
imx7s-colibri-eval-v3.dtb \
|
||||
imx7s-mba7.dtb \
|
||||
imx7s-warp.dtb
|
||||
imx7s-warp.dtb \
|
||||
zero-sugar.dtb
|
||||
dtb-$(CONFIG_SOC_IMX7ULP) += \
|
||||
imx7ulp-evk.dtb \
|
||||
imx7ulp-evk-ft5416.dtb \
|
||||
|
|
|
@ -1101,8 +1101,8 @@
|
|||
clocks = <&clks IMX7D_UART6_ROOT_CLK>,
|
||||
<&clks IMX7D_UART6_ROOT_CLK>;
|
||||
clock-names = "ipg", "per";
|
||||
dmas = <&sdma 32 4 0>, <&sdma 33 4 0>;
|
||||
dma-names = "rx", "tx";
|
||||
/*dmas = <&sdma 32 4 0>, <&sdma 33 4 0>;*/
|
||||
/*dma-names = "rx", "tx";*/
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,955 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* This file is dual-licensed: you can use it either under the terms
|
||||
* of the GPL or the X11 license, at your option. Note that this dual
|
||||
* licensing only applies to this file, and not this project as a
|
||||
* whole.
|
||||
*
|
||||
* a) This file 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 file 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.
|
||||
*
|
||||
* Or, alternatively,
|
||||
*
|
||||
* b) Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
#include "imx7d.dtsi"
|
||||
|
||||
/ {
|
||||
model = "reMarkable 2.0";
|
||||
compatible = "fsl,imx7d-sdb", "fsl,imx7d";
|
||||
|
||||
chosen {
|
||||
stdout-path = &uart1;
|
||||
};
|
||||
|
||||
memory {
|
||||
reg = <0x80000000 0x40000000>;
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
epd-thermal {
|
||||
thermal-sensors = <&epd_pmic>;
|
||||
polling-delay-passive = <30000>;
|
||||
polling-delay = <30000>;
|
||||
trips {
|
||||
trip0 {
|
||||
temperature = <49000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
trip1 {
|
||||
temperature = <50000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
reg_vref_1v8: regulator-vref-1v8 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vref-1v8";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
|
||||
reg_brcm: regulator-brcm {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "brcm_reg";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_brcm_reg>;
|
||||
pinctrl-1 = <&pinctrl_brcm_reg>;
|
||||
gpio = <&gpio6 13 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
startup-delay-us = <150>;
|
||||
};
|
||||
|
||||
reg_digitizer: regulator-digitizer {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "VDD_3V3_DIGITIZER";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_digitizer_reg>;
|
||||
pinctrl-1 = <&pinctrl_digitizer_reg>;
|
||||
gpio = <&gpio1 6 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
startup-delay-us = <100000>; /* 100 ms */
|
||||
};
|
||||
|
||||
reg_touch: regulator-touch {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "VDD_3V3_TOUCH";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_touch_reg>;
|
||||
pinctrl-1 = <&pinctrl_touch_reg>;
|
||||
gpio = <&gpio1 11 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
};
|
||||
|
||||
reg_sdoe: regulator-sdoe {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "SDOE";
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_sdoe_reg>;
|
||||
pinctrl-1 = <&pinctrl_sdoe_reg>;
|
||||
gpio = <&gpio3 27 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
};
|
||||
|
||||
reg_gdoe: regulator-gdoe {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "GDOE";
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_gdoe_reg>;
|
||||
pinctrl-1 = <&pinctrl_gdoe_reg>;
|
||||
gpio = <&gpio3 21 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
wifi_pwrseq: wifi_pwrseq {
|
||||
compatible = "mmc-pwrseq-simple";
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_wifi>;
|
||||
pinctrl-1 = <&pinctrl_wifi>;
|
||||
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
|
||||
clocks = <&clks IMX7D_CLKO2_ROOT_DIV>;
|
||||
clock-names = "ext_clock";
|
||||
};
|
||||
|
||||
otgcontrol: otgcontrol1 {
|
||||
pinctrl-names = "default", "one_wire_uart_tx", "one_wire_uart_rx",
|
||||
"sleep";
|
||||
pinctrl-0 = <&pinctrl_one_wire_gpio>;
|
||||
pinctrl-1 = <&pinctrl_one_wire_uart6_tx>;
|
||||
pinctrl-2 = <&pinctrl_one_wire_uart6_rx>;
|
||||
pinctrl-3 = <&pinctrl_one_wire_gpio>;
|
||||
compatible = "rm-otgcontrol";
|
||||
vbus-supply =<&max77818_fg>;
|
||||
one-wire-tty-name = "ttymxc5";
|
||||
one-wire-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
&cpu0 {
|
||||
cpu-supply = <&buck1_reg>;
|
||||
};
|
||||
|
||||
&clks {
|
||||
assigned-clocks = <&clks IMX7D_CLKO2_ROOT_SRC>,
|
||||
<&clks IMX7D_CLKO2_ROOT_DIV>;
|
||||
assigned-clock-parents = <&clks IMX7D_CKIL>;
|
||||
assigned-clock-rates = <0>, <32768>;
|
||||
};
|
||||
|
||||
&crypto {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&dma_apbh {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
clock-frequency = <400000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_i2c1>;
|
||||
pinctrl-1 = <&pinctrl_i2c1>;
|
||||
status = "okay";
|
||||
|
||||
digitizer: wacom-i2c@09 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_wacom>;
|
||||
pinctrl-1 = <&pinctrl_wacom>;
|
||||
compatible = "wacom,wacom-i2c";
|
||||
reg = <0x09>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <1 2>;
|
||||
flip-tilt-x;
|
||||
flip-tilt-y;
|
||||
flip-pos-x;
|
||||
flip-pos-y;
|
||||
flip-distance;
|
||||
vdd-supply = <®_digitizer>;
|
||||
};
|
||||
};
|
||||
|
||||
&i2c2 {
|
||||
clock-frequency = <100000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_i2c2>;
|
||||
pinctrl-1 = <&pinctrl_i2c2>;
|
||||
status = "okay";
|
||||
|
||||
pmic: bd71815@4b {
|
||||
reg = <0x4b>;
|
||||
compatible = "rohm,bd71815";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_bd71815>;
|
||||
/* PMIC_INT_B GPIO6_IO16 */
|
||||
gpio_intr = <&gpio6 16 0>;
|
||||
|
||||
regulators {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
buck1_reg: regulator@0 {
|
||||
reg = <0>;
|
||||
regulator-compatible = "buck1";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <2000000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
};
|
||||
|
||||
buck2_reg: regulator@1 {
|
||||
reg = <1>;
|
||||
regulator-compatible = "buck2";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <2000000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
};
|
||||
|
||||
buck3_reg: regulator@2 {
|
||||
reg = <2>;
|
||||
regulator-compatible = "buck3";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <2700000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck4_reg: regulator@3 {
|
||||
reg = <3>;
|
||||
regulator-compatible = "buck4";
|
||||
regulator-min-microvolt = <1100000>;
|
||||
regulator-max-microvolt = <1850000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck5_reg: regulator@4 {
|
||||
reg = <4>;
|
||||
regulator-compatible = "buck5";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo1_reg: regulator@5 {
|
||||
reg = <5>;
|
||||
regulator-compatible = "ldo1";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo2_reg: regulator@6 {
|
||||
reg = <6>;
|
||||
regulator-compatible = "ldo2";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo3_reg: regulator@7 {
|
||||
reg = <7>;
|
||||
regulator-compatible = "ldo3";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo4_reg: regulator@8 {
|
||||
reg = <8>;
|
||||
regulator-compatible = "ldo4";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo5_reg: regulator@9 {
|
||||
reg = <9>;
|
||||
regulator-compatible = "ldo5";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
dvref_reg: regulator@10 {
|
||||
reg = <10>;
|
||||
regulator-compatible = "dvref";
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
lpsr_reg: regulator@11 {
|
||||
reg = <11>;
|
||||
regulator-compatible = "lpsr";
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
wled_reg: regulator@12 {
|
||||
reg = <12>;
|
||||
regulator-compatible = "wled";
|
||||
regulator-min-microamp = <10>;
|
||||
regulator-max-microamp = <25000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
max77818@66 {
|
||||
compatible = "maxim,max77818";
|
||||
#address-cells = <0>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_max77818 &pinctrl_chg_stat>;
|
||||
pinctrl-1 = <&pinctrl_max77818 &pinctrl_chg_stat>;
|
||||
reg = <0x66>;
|
||||
|
||||
/* regulators {
|
||||
compatible = "maxim,max77818-regulator";
|
||||
|
||||
reg_safeout1: SAFEOUT1 {
|
||||
regulator-name = "SAFEOUT1";
|
||||
};
|
||||
|
||||
SAFEOUT2 {
|
||||
regulator-name = "SAFEOUT2";
|
||||
};
|
||||
};*/
|
||||
|
||||
max77818_chg: charger {
|
||||
compatible = "maxim,max77818-charger";
|
||||
fast_charge_current = <1500>; /* mA */
|
||||
charge_termination_voltage = <4400>; /* mV */
|
||||
vsys_min = <3400>; /* mV */
|
||||
default_current_limit = <500>; /* mA */
|
||||
input_current_limit_chgin = <1500>; /* mA */
|
||||
input_current_limit_wcin = <1260>; /* mA */
|
||||
topoff_current = <100>; /* mA */
|
||||
chgin-stat-gpios = <&gpio6 19 GPIO_ACTIVE_LOW>;
|
||||
wcin-stat-gpios = <&gpio6 20 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
max77818_fg: fuelgauge {
|
||||
compatible = "maxim,max77818-fuelgauge";
|
||||
power-supplies = <&max77818_chg>;
|
||||
usb-phy1 = <&usbphynop1>;
|
||||
usb-phy2 = <&usbphynop2>;
|
||||
usb_safe_max_current = <500>; /* mA */
|
||||
maxim,over-heat-temp = <600>; /* tenths of degree Celsius */
|
||||
maxim,cold-temp = <(-200)>;
|
||||
maxim,over-volt = <4400>; /* mV */
|
||||
maxim,dead-volt = <3000>;
|
||||
maxim,at-rate = /bits/ 16 <0x1900>;
|
||||
maxim,design-cap = /bits/ 16 <0x1816>;
|
||||
maxim,config = /bits/ 16 <0x0a14>; /* FGCC Enabled */
|
||||
/* maxim,config = /bits/ 16 <0x0214>; /* FGCC disabled */
|
||||
maxim,config2 = /bits/ 16 <0x0050>;
|
||||
maxim,convgcfg = /bits/ 16 <0x2241>;
|
||||
maxim,dpacc = /bits/ 16 <0x3200>;
|
||||
maxim,dqacc = /bits/ 16 <0x0605>;
|
||||
maxim,filter-cfg = /bits/ 16 <0xcea4>;
|
||||
maxim,fullcapnom = /bits/ 16 <0x1816>;
|
||||
maxim,fullcaprep = /bits/ 16 <0x1816>;
|
||||
maxim,full-soc-threshold = /bits/ 16 <0x5f05>;
|
||||
maxim,iavg-empty = /bits/ 16 <0x1345>;
|
||||
maxim,ichg-term = /bits/ 16 <0x0180>;
|
||||
maxim,learn-cfg = /bits/ 16 <0x2602>;
|
||||
maxim,misc-cfg = /bits/ 16 <0x3870>;
|
||||
maxim,qresidual00 = /bits/ 16 <0x1000>;
|
||||
maxim,qresidual10 = /bits/ 16 <0x0880>;
|
||||
maxim,qresidual20 = /bits/ 16 <0x0500>;
|
||||
maxim,qresidual30 = /bits/ 16 <0x0480>;
|
||||
maxim,rcomp0 = /bits/ 16 <0x003a>;
|
||||
maxim,relax-cfg = /bits/ 16 <0x043c>;
|
||||
maxim,smart-chg-cfg = /bits/ 16 <0x0013>; /* SmartChg Enabled, SmartFull enabled */
|
||||
maxim,tempco = /bits/ 16 <0x1728>;
|
||||
maxim,v-empty = /bits/ 16 <0x9157>;
|
||||
maxim,tgain = /bits/ 16 <0xee56>;
|
||||
maxim,toff = /bits/ 16 <0x1da4>;
|
||||
maxim,tcurve = /bits/ 16 <0x0025>;
|
||||
maxim,talrt-th = /bits/ 16 <0x2d0a>; /* T2 = 10 deg/T2 = 45 deg */
|
||||
maxim,talrt-th2 = /bits/ 16 <0x3700>; /* T1 = 0 deg/T4 = 55 deg */
|
||||
maxim,jeita-curr = /bits/ 16 <0x18d0>; /* 19%/100%/19% */
|
||||
maxim,jeita-volt = /bits/ 16 <0xfc9f>; /* 4.4V/4.4V/4.1V */
|
||||
maxim,chargestate0 = /bits/ 16 <0x101e>;
|
||||
maxim,chargestate1 = /bits/ 16 <0x0c1e>;
|
||||
maxim,chargestate2 = /bits/ 16 <0x0c1e>;
|
||||
maxim,chargestate3 = /bits/ 16 <0x0c1e>;
|
||||
maxim,chargestate4 = /bits/ 16 <0x0c1e>;
|
||||
maxim,chargestate5 = /bits/ 16 <0x0c1e>;
|
||||
maxim,chargestate6 = /bits/ 16 <0x0c1e>;
|
||||
maxim,chargestate7 = /bits/ 16 <0x0c1e>;
|
||||
maxim,cell-model-data = /bits/ 16 <
|
||||
0x9d10 0xab90 0xb710 0xb8f0
|
||||
0xba90 0xbc10 0xbd10 0xbe00
|
||||
0xbef0 0xbff0 0xc1b0 0xc490
|
||||
0xc800 0xce40 0xd4c0 0xdb40
|
||||
0x0020 0x0200 0x0c40 0x0d00
|
||||
0x1120 0x1980 0x17b0 0x19f0
|
||||
0x0df0 0x10f0 0x08f0 0x0810
|
||||
0x0770 0x06f0 0x06a0 0x06a0
|
||||
0x0200 0x0200 0x0200 0x0200
|
||||
0x0200 0x0200 0x0200 0x0200
|
||||
0x0200 0x0200 0x0200 0x0200
|
||||
0x0200 0x0200 0x0200 0x0200
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c3 {
|
||||
clock-frequency = <100000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_i2c3>;
|
||||
pinctrl-1 = <&pinctrl_i2c3>;
|
||||
status = "okay";
|
||||
|
||||
tsc@24 {
|
||||
status = "okay";
|
||||
compatible = "cy,cyttsp5_i2c_adapter";
|
||||
reg = <0x24>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <14 2>;
|
||||
cy,adapter_id = "cyttsp5_i2c_adapter";
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_touch>;
|
||||
pinctrl-1 = <&pinctrl_touch>;
|
||||
vdd-supply = <®_touch>;
|
||||
|
||||
cy,core {
|
||||
cy,name = "cyttsp5_core";
|
||||
|
||||
cy,irq_gpio = <&gpio1 14 0>;
|
||||
cy,rst_gpio = <&gpio1 13 0>;
|
||||
cy,hid_desc_register = <1>;
|
||||
/* CY_CORE_FLAG_RESTORE_PARAMETERS & CY_CORE_FLAG_POWEROFF_ON_SLEEP */
|
||||
cy,flags = <0x6>;
|
||||
/* CY_CORE_EWG_NONE */
|
||||
cy,easy_wakeup_gesture = <0>;
|
||||
cy,btn_keys = <172 /* KEY_HOMEPAGE */
|
||||
/* previously was KEY_HOME, new Android versions use KEY_HOMEPAGE */
|
||||
139 /* KEY_MENU */
|
||||
158 /* KEY_BACK */
|
||||
217 /* KEY_SEARCH */
|
||||
114 /* KEY_VOLUMEDOWN */
|
||||
115 /* KEY_VOLUMEUP */
|
||||
212 /* KEY_CAMERA */
|
||||
116>; /* KEY_POWER */
|
||||
cy,btn_keys-tag = <0>;
|
||||
cy,fb_blanking_disabled;
|
||||
|
||||
cy,mt {
|
||||
cy,name = "cyttsp5_mt";
|
||||
|
||||
cy,inp_dev_name = "cyttsp5_mt";
|
||||
cy,flags = <0>;
|
||||
cy,abs =
|
||||
/* ABS_MT_POSITION_X, CY_ABS_MIN_X, CY_ABS_MAX_X, 0, 0 */
|
||||
<0x35 0 880 0 0
|
||||
/* ABS_MT_POSITION_Y, CY_ABS_MIN_Y, CY_ABS_MAX_Y, 0, 0 */
|
||||
0x36 0 1280 0 0
|
||||
/* ABS_MT_PRESSURE, CY_ABS_MIN_P, CY_ABS_MAX_P, 0, 0 */
|
||||
0x3a 0 255 0 0
|
||||
/* CY_IGNORE_VALUE, CY_ABS_MIN_W, CY_ABS_MAX_W, 0, 0 */
|
||||
0xffff 0 255 0 0
|
||||
/* ABS_MT_TRACKING_ID, CY_ABS_MIN_T, CY_ABS_MAX_T, 0, 0 */
|
||||
0x39 0 15 0 0
|
||||
/* ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0 */
|
||||
0x30 0 255 0 0
|
||||
/* ABS_MT_TOUCH_MINOR, 0, 255, 0, 0 */
|
||||
0x31 0 255 0 0
|
||||
/* ABS_MT_ORIENTATION, -127, 127, 0, 0 */
|
||||
0x34 0xffffff81 127 0 0
|
||||
/* ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0 */
|
||||
0x37 0 1 0 0
|
||||
/* ABS_DISTANCE, 0, 255, 0, 0 */
|
||||
0x19 0 255 0 0>;
|
||||
|
||||
cy,vkeys_x = <720>;
|
||||
cy,vkeys_y = <1280>;
|
||||
|
||||
cy,virtual_keys = /* KeyCode CenterX CenterY Width Height */
|
||||
/* KEY_BACK */
|
||||
<158 1360 90 160 180
|
||||
/* KEY_MENU */
|
||||
139 1360 270 160 180
|
||||
/* KEY_HOMEPAGE */
|
||||
172 1360 450 160 180
|
||||
/* KEY SEARCH */
|
||||
217 1360 630 160 180>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c4 {
|
||||
clock-frequency = <100000>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_i2c4>;
|
||||
pinctrl-1 = <&pinctrl_i2c4>;
|
||||
status = "okay";
|
||||
|
||||
epd_pmic: sy7636a@62 {
|
||||
compatible = "silergy,sy7636a";
|
||||
reg = <0x62>;
|
||||
status = "okay";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_epdpmic>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
|
||||
epd-pwr-good-gpios = <&gpio6 21 GPIO_ACTIVE_HIGH>;
|
||||
regulators {
|
||||
compatible = "silergy,sy7636a-regulator";
|
||||
reg_epdpmic: vcom {
|
||||
regulator-name = "vcom";
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
&lcdif {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_lcdif>;
|
||||
pinctrl-1 = <&pinctrl_lcdif>;
|
||||
/* TODO: enable-gpio = <&extended_io 7 GPIO_ACTIVE_LOW>; */
|
||||
lcd-supply = <®_epdpmic>;
|
||||
lcd2-supply = <®_sdoe>;
|
||||
display = <&display0>;
|
||||
prevent-frying-pan;
|
||||
status = "okay";
|
||||
|
||||
display0: display {
|
||||
bits-per-pixel = <24>;
|
||||
bus-width = <24>;
|
||||
|
||||
display-timings {
|
||||
native-mode = <&timing0>;
|
||||
|
||||
timing0: timing0 {
|
||||
clock-frequency = <40000000>;
|
||||
hactive = <334>;
|
||||
vactive = <1405>;
|
||||
hfront-porch = <1>;
|
||||
hback-porch = <1>;
|
||||
hsync-len = <1>;
|
||||
vback-porch = <1>;
|
||||
vfront-porch = <1>;
|
||||
vsync-len = <1>;
|
||||
hsync-active = <0>;
|
||||
vsync-active = <0>;
|
||||
de-active = <1>;
|
||||
pixelclk-active = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&mu {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&sdma {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart1 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_uart1>;
|
||||
pinctrl-1 = <&pinctrl_uart1>;
|
||||
assigned-clocks = <&clks IMX7D_UART1_ROOT_SRC>;
|
||||
assigned-clock-parents = <&clks IMX7D_OSC_24M_CLK>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart6 {
|
||||
/* Pinctrl is defined under 'otgcontrol' driver, which will switch the pinmuxing as required */
|
||||
assigned-clocks = <&clks IMX7D_UART6_ROOT_SRC>;
|
||||
assigned-clock-parents = <&clks IMX7D_OSC_24M_CLK>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usbotg1 {
|
||||
extcon = <0 &otgcontrol>;
|
||||
srp-disable;
|
||||
hnp-disable;
|
||||
dp-disable;
|
||||
phy-charger-detection;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usbotg2 {
|
||||
srp-disable;
|
||||
hnp-disable;
|
||||
asp-disable;
|
||||
phy-charger-detection;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usdhc2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pinctrl-names = "default", "state_100mhz", "sleep";
|
||||
pinctrl-0 = <&pinctrl_usdhc2>;
|
||||
pinctrl-1 = <&pinctrl_usdhc2_100mhz>;
|
||||
pinctrl-2 = <&pinctrl_usdhc2>;
|
||||
mmc-pwrseq = <&wifi_pwrseq>;
|
||||
vmmc-supply = <®_brcm>;
|
||||
bus-width = <4>;
|
||||
non-removable;
|
||||
keep-power-in-suspend;
|
||||
cap-power-off-card;
|
||||
status = "okay";
|
||||
|
||||
brcmf: bcrmf@1 {
|
||||
reg = <1>;
|
||||
compatible = "brcm,bcm4329-fmac";
|
||||
};
|
||||
};
|
||||
|
||||
&usdhc3 {
|
||||
pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep";
|
||||
pinctrl-0 = <&pinctrl_usdhc3>;
|
||||
pinctrl-1 = <&pinctrl_usdhc3_100mhz>;
|
||||
pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
|
||||
pinctrl-3 = <&pinctrl_usdhc3>;
|
||||
assigned-clocks = <&clks IMX7D_USDHC3_ROOT_CLK>;
|
||||
assigned-clock-rates = <400000000>;
|
||||
bus-width = <8>;
|
||||
non-removable;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&wdog1 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&pinctrl_wdog>;
|
||||
pinctrl-1 = <&pinctrl_wdog>;
|
||||
fsl,ext-reset-output;
|
||||
};
|
||||
|
||||
&iomuxc_lpsr {
|
||||
pinctrl_wacom: wacomgrp {
|
||||
fsl,pins = <
|
||||
/*MX7D_PAD_LPSR_GPIO1_IO00__GPIO1_IO0 0x00000074 /* WACOM RESET */
|
||||
MX7D_PAD_LPSR_GPIO1_IO01__GPIO1_IO1 0x00000034 /* WACOM INT */
|
||||
MX7D_PAD_LPSR_GPIO1_IO04__GPIO1_IO4 0x00000074 /* PDCTB */
|
||||
/*MX7D_PAD_LPSR_GPIO1_IO05__GPIO1_IO5 0x00000014 /* FWE */
|
||||
/*MX7D_PAD_LPSR_GPIO1_IO06__GPIO1_IO6 0x00000014 /* WACOM PWR ENABLE */
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_digitizer_reg: digitizerreggrp {
|
||||
fsl,pins = <
|
||||
/* DIGITIZER_PWR_EN */
|
||||
MX7D_PAD_LPSR_GPIO1_IO06__GPIO1_IO6 0x14
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_max77818: max77818grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_LPSR_GPIO1_IO07__GPIO1_IO7 0x74
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&iomuxc {
|
||||
pinctrl_bd71815: bd71815grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SAI1_RX_SYNC__GPIO6_IO16 0x59
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_touch: touchgrp {
|
||||
fsl,pins = <
|
||||
/* CYTTSP interrupt */
|
||||
MX7D_PAD_GPIO1_IO14__GPIO1_IO14 0x17000
|
||||
/* CYTTSP reset */
|
||||
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x110b0
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_i2c1: i2c1grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_I2C1_SDA__I2C1_SDA 0x4000007f
|
||||
MX7D_PAD_I2C1_SCL__I2C1_SCL 0x4000007f
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_i2c2: i2c2grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_I2C2_SDA__I2C2_SDA 0x4000007f
|
||||
MX7D_PAD_I2C2_SCL__I2C2_SCL 0x4000007f
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_i2c3: i2c3grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_I2C3_SDA__I2C3_SDA 0x4000007f
|
||||
MX7D_PAD_I2C3_SCL__I2C3_SCL 0x4000007f
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_i2c4: i2c4grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_I2C4_SDA__I2C4_SDA 0x4000007f
|
||||
MX7D_PAD_I2C4_SCL__I2C4_SCL 0x4000007f
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_lcdif: lcdifgrp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_LCD_DATA00__LCD_DATA0 0x79
|
||||
MX7D_PAD_LCD_DATA01__LCD_DATA1 0x79
|
||||
MX7D_PAD_LCD_DATA02__LCD_DATA2 0x79
|
||||
MX7D_PAD_LCD_DATA03__LCD_DATA3 0x79
|
||||
MX7D_PAD_LCD_DATA04__LCD_DATA4 0x79
|
||||
MX7D_PAD_LCD_DATA05__LCD_DATA5 0x79
|
||||
MX7D_PAD_LCD_DATA06__LCD_DATA6 0x79
|
||||
MX7D_PAD_LCD_DATA07__LCD_DATA7 0x79
|
||||
MX7D_PAD_LCD_DATA08__LCD_DATA8 0x79
|
||||
MX7D_PAD_LCD_DATA09__LCD_DATA9 0x79
|
||||
MX7D_PAD_LCD_DATA10__LCD_DATA10 0x79
|
||||
MX7D_PAD_LCD_DATA11__LCD_DATA11 0x79
|
||||
MX7D_PAD_LCD_DATA12__LCD_DATA12 0x79
|
||||
MX7D_PAD_LCD_DATA13__LCD_DATA13 0x79
|
||||
MX7D_PAD_LCD_DATA14__LCD_DATA14 0x79
|
||||
MX7D_PAD_LCD_DATA15__LCD_DATA15 0x79
|
||||
|
||||
MX7D_PAD_LCD_DATA17__LCD_DATA17 0x79
|
||||
MX7D_PAD_LCD_DATA18__LCD_DATA18 0x79
|
||||
MX7D_PAD_LCD_DATA19__LCD_DATA19 0x79
|
||||
MX7D_PAD_LCD_DATA20__LCD_DATA20 0x79
|
||||
MX7D_PAD_LCD_DATA21__LCD_DATA21 0x79
|
||||
|
||||
MX7D_PAD_LCD_DATA23__LCD_DATA23 0x79
|
||||
MX7D_PAD_LCD_CLK__LCD_CLK 0x79
|
||||
MX7D_PAD_LCD_ENABLE__LCD_ENABLE 0x79
|
||||
MX7D_PAD_LCD_VSYNC__LCD_VSYNC 0x79
|
||||
MX7D_PAD_LCD_HSYNC__LCD_HSYNC 0x79
|
||||
MX7D_PAD_LCD_RESET__LCD_RESET 0x79
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_uart1: uart1grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_UART1_TX_DATA__UART1_DCE_TX 0x79
|
||||
MX7D_PAD_UART1_RX_DATA__UART1_DCE_RX 0x79
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_usdhc2: usdhc2grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SD2_CMD__SD2_CMD 0x59
|
||||
MX7D_PAD_SD2_CLK__SD2_CLK 0x19
|
||||
MX7D_PAD_SD2_DATA0__SD2_DATA0 0x59
|
||||
MX7D_PAD_SD2_DATA1__SD2_DATA1 0x59
|
||||
MX7D_PAD_SD2_DATA2__SD2_DATA2 0x59
|
||||
MX7D_PAD_SD2_DATA3__SD2_DATA3 0x59
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_usdhc2_100mhz: usdhc2grp_100mhz {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SD2_CMD__SD2_CMD 0x5a
|
||||
MX7D_PAD_SD2_CLK__SD2_CLK 0x1a
|
||||
MX7D_PAD_SD2_DATA0__SD2_DATA0 0x5a
|
||||
MX7D_PAD_SD2_DATA1__SD2_DATA1 0x5a
|
||||
MX7D_PAD_SD2_DATA2__SD2_DATA2 0x5a
|
||||
MX7D_PAD_SD2_DATA3__SD2_DATA3 0x5a
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_usdhc2_200mhz: usdhc2grp_200mhz {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SD2_CMD__SD2_CMD 0x5b
|
||||
MX7D_PAD_SD2_CLK__SD2_CLK 0x1b
|
||||
MX7D_PAD_SD2_DATA0__SD2_DATA0 0x5b
|
||||
MX7D_PAD_SD2_DATA1__SD2_DATA1 0x5b
|
||||
MX7D_PAD_SD2_DATA2__SD2_DATA2 0x5b
|
||||
MX7D_PAD_SD2_DATA3__SD2_DATA3 0x5b
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
pinctrl_usdhc3: usdhc3grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SD3_CMD__SD3_CMD 0x59
|
||||
MX7D_PAD_SD3_CLK__SD3_CLK 0x19
|
||||
MX7D_PAD_SD3_DATA0__SD3_DATA0 0x59
|
||||
MX7D_PAD_SD3_DATA1__SD3_DATA1 0x59
|
||||
MX7D_PAD_SD3_DATA2__SD3_DATA2 0x59
|
||||
MX7D_PAD_SD3_DATA3__SD3_DATA3 0x59
|
||||
MX7D_PAD_SD3_DATA4__SD3_DATA4 0x59
|
||||
MX7D_PAD_SD3_DATA5__SD3_DATA5 0x59
|
||||
MX7D_PAD_SD3_DATA6__SD3_DATA6 0x59
|
||||
MX7D_PAD_SD3_DATA7__SD3_DATA7 0x59
|
||||
MX7D_PAD_SD3_STROBE__SD3_STROBE 0x19
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_usdhc3_100mhz: usdhc3grp_100mhz {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SD3_CMD__SD3_CMD 0x5a
|
||||
MX7D_PAD_SD3_CLK__SD3_CLK 0x1a
|
||||
MX7D_PAD_SD3_DATA0__SD3_DATA0 0x5a
|
||||
MX7D_PAD_SD3_DATA1__SD3_DATA1 0x5a
|
||||
MX7D_PAD_SD3_DATA2__SD3_DATA2 0x5a
|
||||
MX7D_PAD_SD3_DATA3__SD3_DATA3 0x5a
|
||||
MX7D_PAD_SD3_DATA4__SD3_DATA4 0x5a
|
||||
MX7D_PAD_SD3_DATA5__SD3_DATA5 0x5a
|
||||
MX7D_PAD_SD3_DATA6__SD3_DATA6 0x5a
|
||||
MX7D_PAD_SD3_DATA7__SD3_DATA7 0x5a
|
||||
MX7D_PAD_SD3_STROBE__SD3_STROBE 0x1a
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_usdhc3_200mhz: usdhc3grp_200mhz {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SD3_CMD__SD3_CMD 0x5b
|
||||
MX7D_PAD_SD3_CLK__SD3_CLK 0x1b
|
||||
MX7D_PAD_SD3_DATA0__SD3_DATA0 0x5b
|
||||
MX7D_PAD_SD3_DATA1__SD3_DATA1 0x5b
|
||||
MX7D_PAD_SD3_DATA2__SD3_DATA2 0x5b
|
||||
MX7D_PAD_SD3_DATA3__SD3_DATA3 0x5b
|
||||
MX7D_PAD_SD3_DATA4__SD3_DATA4 0x5b
|
||||
MX7D_PAD_SD3_DATA5__SD3_DATA5 0x5b
|
||||
MX7D_PAD_SD3_DATA6__SD3_DATA6 0x5b
|
||||
MX7D_PAD_SD3_DATA7__SD3_DATA7 0x5b
|
||||
MX7D_PAD_SD3_STROBE__SD3_STROBE 0x1b
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_brcm_reg: brcmreggrp {
|
||||
fsl,pins = <
|
||||
/* WIFI_PWR_EN */
|
||||
MX7D_PAD_SAI1_TX_BCLK__GPIO6_IO13 0x14
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_wifi: wifigrp {
|
||||
fsl,pins = <
|
||||
/* WiFi Reg On */
|
||||
MX7D_PAD_SD2_CD_B__GPIO5_IO9 0x00000014
|
||||
/* WiFi Sleep 32k */
|
||||
MX7D_PAD_SD1_WP__CCM_CLKO2 0x00000014
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_epdpmic: epdpmicgrp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_SAI2_RX_DATA__GPIO6_IO21 0x00000074
|
||||
MX7D_PAD_ENET1_RGMII_TXC__GPIO7_IO11 0x00000014
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_wdog: wdoggrp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_ENET1_COL__WDOG1_WDOG_ANY 0x74
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_one_wire_gpio: one_wire_gpio_grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_EPDC_DATA08__GPIO2_IO8 0x00000004
|
||||
MX7D_PAD_EPDC_DATA09__GPIO2_IO9 0x00000004
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_one_wire_uart6_tx: one_wire_uart6_tx_grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_EPDC_DATA08__GPIO2_IO8 0x00000004
|
||||
MX7D_PAD_EPDC_DATA09__UART6_DCE_TX 0x00000004
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_one_wire_uart6_rx: one_wire_uart6_rx_grp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_EPDC_DATA08__UART6_DCE_RX 0x00000004
|
||||
MX7D_PAD_EPDC_DATA09__GPIO2_IO9 0x00000004
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_touch_reg: touchreggrp {
|
||||
fsl,pins = <
|
||||
/* TOUCH_PWR_EN */
|
||||
MX7D_PAD_GPIO1_IO11__GPIO1_IO11 0x14
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_sdoe_reg: sdoereggrp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_LCD_DATA22__GPIO3_IO27 0x74
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_gdoe_reg: gdoereggrp {
|
||||
fsl,pins = <
|
||||
MX7D_PAD_LCD_DATA16__GPIO3_IO21 0x74
|
||||
>;
|
||||
};
|
||||
|
||||
pinctrl_chg_stat: chg_stat_grp {
|
||||
fsl,pins = <
|
||||
/* CHGIN OK/CONNECTED */
|
||||
MX7D_PAD_SAI2_TX_SYNC__GPIO6_IO19 0x00000074
|
||||
|
||||
/* WCIN (USB-C) OK/CONNECTED */
|
||||
MX7D_PAD_SAI2_TX_BCLK__GPIO6_IO20 0x00000074
|
||||
|
||||
>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,312 @@
|
|||
CONFIG_KERNEL_LZO=y
|
||||
CONFIG_SYSVIPC=y
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_IKCONFIG=y
|
||||
CONFIG_IKCONFIG_PROC=y
|
||||
CONFIG_LOG_BUF_SHIFT=18
|
||||
CONFIG_CGROUPS=y
|
||||
CONFIG_RELAY=y
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
CONFIG_EXPERT=y
|
||||
CONFIG_KALLSYMS_ALL=y
|
||||
CONFIG_PERF_EVENTS=y
|
||||
# CONFIG_SLUB_DEBUG is not set
|
||||
# CONFIG_COMPAT_BRK is not set
|
||||
CONFIG_MODULES=y
|
||||
CONFIG_MODULE_UNLOAD=y
|
||||
CONFIG_MODVERSIONS=y
|
||||
CONFIG_MODULE_SRCVERSION_ALL=y
|
||||
# CONFIG_BLK_DEV_BSG is not set
|
||||
CONFIG_ARCH_MXC=y
|
||||
CONFIG_SOC_IMX50=y
|
||||
CONFIG_SOC_IMX53=y
|
||||
CONFIG_SOC_IMX6Q=y
|
||||
CONFIG_SOC_IMX6SL=y
|
||||
CONFIG_SOC_IMX6SX=y
|
||||
CONFIG_SOC_IMX6ULL=y
|
||||
CONFIG_SOC_IMX7D=y
|
||||
CONFIG_SOC_IMX6SLL=y
|
||||
CONFIG_SOC_IMX7ULP=y
|
||||
CONFIG_SOC_VF610=y
|
||||
# CONFIG_SWP_EMULATE is not set
|
||||
CONFIG_SMP=y
|
||||
CONFIG_VMSPLIT_2G=y
|
||||
CONFIG_ARM_PSCI=y
|
||||
CONFIG_PREEMPT=y
|
||||
CONFIG_HIGHMEM=y
|
||||
CONFIG_CMA=y
|
||||
CONFIG_CMDLINE="noinitrd console=ttymxc0,115200"
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
|
||||
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
|
||||
CONFIG_CPUFREQ_DT=y
|
||||
CONFIG_ARM_IMX6Q_CPUFREQ=y
|
||||
CONFIG_ARM_IMX7ULP_CPUFREQ=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_VFP=y
|
||||
CONFIG_NEON=y
|
||||
CONFIG_BINFMT_MISC=m
|
||||
CONFIG_PM_DEBUG=y
|
||||
CONFIG_PM_TEST_SUSPEND=y
|
||||
CONFIG_PM_RESLEEP_LOCK=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_UNIX=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_PNP=y
|
||||
CONFIG_IP_PNP_DHCP=y
|
||||
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
||||
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
||||
# CONFIG_INET_XFRM_MODE_BEET is not set
|
||||
CONFIG_VLAN_8021Q=y
|
||||
CONFIG_LLC2=y
|
||||
CONFIG_CFG80211=y
|
||||
CONFIG_NL80211_TESTMODE=y
|
||||
CONFIG_CFG80211_INTERNAL_REGDB=y
|
||||
CONFIG_CFG80211_WEXT=y
|
||||
CONFIG_MAC80211=y
|
||||
CONFIG_RFKILL=y
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
# CONFIG_STANDALONE is not set
|
||||
CONFIG_DMA_CMA=y
|
||||
CONFIG_CMA_SIZE_MBYTES=0
|
||||
CONFIG_IMX_WEIM=y
|
||||
CONFIG_CONNECTOR=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_SIZE=65536
|
||||
CONFIG_SENSORS_FXOS8700=y
|
||||
CONFIG_SENSORS_FXAS2100X=y
|
||||
CONFIG_EEPROM_AT24=y
|
||||
CONFIG_RM_OTGCONTROL=y
|
||||
CONFIG_NETDEVICES=y
|
||||
# CONFIG_NET_CORE is not set
|
||||
# CONFIG_ETHERNET is not set
|
||||
CONFIG_MICREL_PHY=y
|
||||
CONFIG_USB_KAWETH=y
|
||||
CONFIG_USB_PEGASUS=y
|
||||
CONFIG_USB_RTL8150=y
|
||||
CONFIG_USB_RTL8152=y
|
||||
CONFIG_USB_USBNET=y
|
||||
CONFIG_USB_NET_CDC_EEM=m
|
||||
CONFIG_ATH6KL=m
|
||||
CONFIG_ATH6KL_SDIO=m
|
||||
CONFIG_BRCMFMAC=m
|
||||
CONFIG_HOSTAP=y
|
||||
CONFIG_INPUT_EVDEV=y
|
||||
CONFIG_KEYBOARD_GPIO=y
|
||||
CONFIG_KEYBOARD_RPMSG=y
|
||||
CONFIG_KEYBOARD_PF1550_ONKEY=y
|
||||
CONFIG_KEYBOARD_IMX=y
|
||||
CONFIG_MOUSE_PS2=m
|
||||
CONFIG_MOUSE_PS2_ELANTECH=y
|
||||
CONFIG_INPUT_TOUCHSCREEN=y
|
||||
CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5=y
|
||||
CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT=y
|
||||
CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_I2C=y
|
||||
CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER=y
|
||||
CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE=y
|
||||
CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE=y
|
||||
CONFIG_TOUCHSCREEN_EGALAX=y
|
||||
CONFIG_TOUCHSCREEN_ELAN_TS=y
|
||||
CONFIG_TOUCHSCREEN_WACOM_I2C=y
|
||||
CONFIG_TOUCHSCREEN_MAX11801=y
|
||||
CONFIG_TOUCHSCREEN_IMX6UL_TSC=y
|
||||
CONFIG_TOUCHSCREEN_MC13783=y
|
||||
CONFIG_TOUCHSCREEN_TSC2007=y
|
||||
CONFIG_SERIO_SERPORT=m
|
||||
# CONFIG_LEGACY_PTYS is not set
|
||||
CONFIG_SERIAL_IMX=y
|
||||
CONFIG_SERIAL_IMX_CONSOLE=y
|
||||
CONFIG_SERIAL_FSL_LPUART=y
|
||||
CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
|
||||
CONFIG_FSL_OTP=y
|
||||
CONFIG_HW_RANDOM_IMX_RNG=y
|
||||
# CONFIG_I2C_COMPAT is not set
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
# CONFIG_I2C_HELPER_AUTO is not set
|
||||
CONFIG_I2C_ALGOPCF=m
|
||||
CONFIG_I2C_ALGOPCA=m
|
||||
CONFIG_I2C_IMX=y
|
||||
CONFIG_I2C_IMX_LPI2C=y
|
||||
CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
CONFIG_GPIO_IMX_RPMSG=y
|
||||
CONFIG_GPIO_MAX732X=y
|
||||
CONFIG_GPIO_PCA953X=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_SYSCON_POWEROFF=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_BATTERY_MAX77818=y
|
||||
CONFIG_CHARGER_PF1550=y
|
||||
CONFIG_SABRESD_MAX8903=y
|
||||
CONFIG_CHARGER_MAX77818=y
|
||||
CONFIG_SENSORS_SY7636A=y
|
||||
CONFIG_THERMAL=y
|
||||
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=10000
|
||||
CONFIG_THERMAL_WRITABLE_TRIPS=y
|
||||
CONFIG_THERMAL_GOV_USER_SPACE=y
|
||||
CONFIG_CPU_THERMAL=y
|
||||
CONFIG_IMX_THERMAL=y
|
||||
CONFIG_DEVICE_THERMAL=y
|
||||
CONFIG_SY7636A_THERMAL=y
|
||||
CONFIG_WATCHDOG=y
|
||||
CONFIG_IMX2_WDT=y
|
||||
CONFIG_IMX7ULP_WDT=y
|
||||
CONFIG_MFD_DA9052_I2C=y
|
||||
CONFIG_MFD_MC13XXX_I2C=y
|
||||
CONFIG_MFD_PF1550=y
|
||||
CONFIG_MFD_MAX77818=y
|
||||
CONFIG_MFD_SY7636A=y
|
||||
CONFIG_MFD_BD7181X=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_REGULATOR_ANATOP=y
|
||||
CONFIG_REGULATOR_BD7181X=y
|
||||
CONFIG_REGULATOR_DA9052=y
|
||||
CONFIG_REGULATOR_GPIO=y
|
||||
CONFIG_REGULATOR_MAX77818=y
|
||||
CONFIG_REGULATOR_SY7636A=y
|
||||
# CONFIG_RC_CORE is not set
|
||||
CONFIG_FB=y
|
||||
# CONFIG_FB_MX3 is not set
|
||||
CONFIG_FB_MXS=y
|
||||
# CONFIG_FB_MXC_EDID is not set
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_OTG_WHITELIST=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_MXC=y
|
||||
CONFIG_USB_HCD_TEST_MODE=y
|
||||
CONFIG_USB_ACM=m
|
||||
CONFIG_USB_CHIPIDEA=y
|
||||
CONFIG_USB_CHIPIDEA_UDC=y
|
||||
CONFIG_USB_CHIPIDEA_HOST=y
|
||||
CONFIG_USB_SERIAL=m
|
||||
CONFIG_USB_SERIAL_GENERIC=y
|
||||
CONFIG_USB_SERIAL_FTDI_SIO=m
|
||||
CONFIG_USB_SERIAL_OPTION=m
|
||||
CONFIG_USB_TEST=m
|
||||
CONFIG_USB_EHSET_TEST_FIXTURE=y
|
||||
CONFIG_NOP_USB_XCEIV=y
|
||||
CONFIG_USB_MXS_PHY=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_CONFIGFS=y
|
||||
CONFIG_USB_CONFIGFS_SERIAL=y
|
||||
CONFIG_USB_CONFIGFS_ACM=y
|
||||
CONFIG_USB_CONFIGFS_OBEX=y
|
||||
CONFIG_USB_CONFIGFS_NCM=y
|
||||
CONFIG_USB_CONFIGFS_ECM=y
|
||||
CONFIG_USB_CONFIGFS_ECM_SUBSET=y
|
||||
CONFIG_USB_CONFIGFS_RNDIS=y
|
||||
CONFIG_USB_CONFIGFS_EEM=y
|
||||
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
|
||||
CONFIG_FSL_UTP=y
|
||||
CONFIG_USB_CONFIGFS_F_LB_SS=y
|
||||
CONFIG_USB_CONFIGFS_F_FS=y
|
||||
CONFIG_USB_ETH=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_MMC_SDHCI_ESDHC_IMX=y
|
||||
CONFIG_MXC_SIM=y
|
||||
CONFIG_MXC_SIMv2=y
|
||||
CONFIG_MXC_IPU=y
|
||||
CONFIG_MXC_IPU_V3_PRE=y
|
||||
CONFIG_MXC_MIPI_CSI2=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
CONFIG_LEDS_TRIGGER_ONESHOT=y
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
|
||||
CONFIG_LEDS_TRIGGER_GPIO=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
|
||||
CONFIG_RTC_DRV_MC13XXX=y
|
||||
CONFIG_RTC_DRV_MXC=y
|
||||
CONFIG_RTC_DRV_SNVS=y
|
||||
CONFIG_RTC_DRV_IMX_RPMSG=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_FSL_EDMA=y
|
||||
CONFIG_IMX_SDMA=y
|
||||
CONFIG_MXS_DMA=y
|
||||
CONFIG_MXC_PXP_V2=y
|
||||
CONFIG_MXC_PXP_V3=y
|
||||
CONFIG_DMATEST=m
|
||||
CONFIG_SYNC_FILE=y
|
||||
# CONFIG_IOMMU_SUPPORT is not set
|
||||
CONFIG_EXTCON_USB_GPIO=y
|
||||
CONFIG_TEE=y
|
||||
CONFIG_OPTEE=y
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT2_FS_XATTR=y
|
||||
CONFIG_EXT2_FS_POSIX_ACL=y
|
||||
CONFIG_EXT2_FS_SECURITY=y
|
||||
CONFIG_EXT3_FS=y
|
||||
CONFIG_EXT3_FS_POSIX_ACL=y
|
||||
CONFIG_EXT3_FS_SECURITY=y
|
||||
CONFIG_QUOTA=y
|
||||
CONFIG_QUOTA_NETLINK_INTERFACE=y
|
||||
# CONFIG_PRINT_QUOTA_WARNING is not set
|
||||
CONFIG_AUTOFS4_FS=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_ISO9660_FS=m
|
||||
CONFIG_JOLIET=y
|
||||
CONFIG_ZISOFS=y
|
||||
CONFIG_UDF_FS=m
|
||||
CONFIG_MSDOS_FS=m
|
||||
CONFIG_VFAT_FS=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_NFS_FS=y
|
||||
CONFIG_NFS_V3_ACL=y
|
||||
CONFIG_NFS_V4=y
|
||||
CONFIG_ROOT_NFS=y
|
||||
CONFIG_NLS_DEFAULT="cp437"
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_ASCII=y
|
||||
CONFIG_NLS_ISO8859_1=y
|
||||
CONFIG_NLS_ISO8859_15=m
|
||||
CONFIG_NLS_UTF8=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_DYNAMIC_DEBUG=y
|
||||
CONFIG_DEBUG_FS=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
# CONFIG_SCHED_DEBUG is not set
|
||||
# CONFIG_DEBUG_BUGVERBOSE is not set
|
||||
# CONFIG_FTRACE is not set
|
||||
CONFIG_MEMTEST=y
|
||||
CONFIG_SECURITYFS=y
|
||||
CONFIG_CRYPTO_ECDH=y
|
||||
CONFIG_CRYPTO_USER=y
|
||||
CONFIG_CRYPTO_TEST=m
|
||||
CONFIG_CRYPTO_CTS=y
|
||||
CONFIG_CRYPTO_LRW=y
|
||||
CONFIG_CRYPTO_XTS=y
|
||||
CONFIG_CRYPTO_MD4=y
|
||||
CONFIG_CRYPTO_MD5=y
|
||||
CONFIG_CRYPTO_RMD128=y
|
||||
CONFIG_CRYPTO_RMD160=y
|
||||
CONFIG_CRYPTO_RMD256=y
|
||||
CONFIG_CRYPTO_RMD320=y
|
||||
CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CRYPTO_TGR192=y
|
||||
CONFIG_CRYPTO_WP512=y
|
||||
CONFIG_CRYPTO_BLOWFISH=y
|
||||
CONFIG_CRYPTO_CAMELLIA=y
|
||||
CONFIG_CRYPTO_TWOFISH=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_DEV_FSL_CAAM=y
|
||||
CONFIG_CRYPTO_DEV_MXS_DCP=y
|
||||
CONFIG_CRC_CCITT=m
|
||||
CONFIG_CRC_T10DIF=y
|
||||
CONFIG_CRC7=m
|
||||
CONFIG_LIBCRC32C=m
|
|
@ -132,6 +132,12 @@
|
|||
#define M4_RCR_GO 0xAA
|
||||
#define M4_OCRAMS_RESERVED_SIZE 0xc
|
||||
|
||||
#define SNVS_LPCR 0x38
|
||||
#define SNVS_LPCR_ON_TIME_MASK GENMASK(21, 20)
|
||||
#define SNVS_LPCR_ON_TIME_SHIFT 20
|
||||
#define SNVS_LPCR_ON_TIME_50MS 0x1
|
||||
#define SNVS_LPCR_ON_TIME_500MS 0x0
|
||||
|
||||
extern unsigned long iram_tlb_base_addr;
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
|
@ -157,7 +163,7 @@ static void __iomem *system_counter_cmp_base;
|
|||
static void __iomem *gpio1_base;
|
||||
static void (*imx7_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
|
||||
struct imx7_cpu_pm_info *pm_info;
|
||||
static bool lpsr_enabled;
|
||||
static bool lpsr_enabled = true;
|
||||
static u32 iomuxc_gpr[MAX_IOMUXC_GPR];
|
||||
static u32 uart1_io[MAX_UART_IO];
|
||||
static u32 ccm_lpcg[MAX_CCM_LPCG];
|
||||
|
@ -391,6 +397,71 @@ static const char * const low_power_ocram_match[] __initconst = {
|
|||
NULL
|
||||
};
|
||||
|
||||
#define CCGR_WDOG1 0x49c0
|
||||
#define CCGR_WDOG2 0x49d0
|
||||
#define CCGR_WDOG3 0x49e0
|
||||
#define CCGR_WDOG4 0x49f0
|
||||
#define ROOT_WDOG 0xbb80
|
||||
|
||||
#define WDOG_WMCR 0x8
|
||||
|
||||
static void __iomem *wdog1_base;
|
||||
static void __iomem *wdog2_base;
|
||||
static void __iomem *wdog3_base;
|
||||
static void __iomem *wdog4_base;
|
||||
|
||||
static inline void disable_wdog_powerdown_single(int id)
|
||||
{
|
||||
void __iomem *ccm_root, *ccm_ccgr, *wdog_wmcr;
|
||||
u32 val_root, val_ccgr;
|
||||
|
||||
ccm_root = pm_info->ccm_base.vbase + ROOT_WDOG;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
ccm_ccgr = pm_info->ccm_base.vbase + CCGR_WDOG1;
|
||||
wdog_wmcr = wdog1_base + WDOG_WMCR;
|
||||
break;
|
||||
case 1:
|
||||
ccm_ccgr = pm_info->ccm_base.vbase + CCGR_WDOG2;
|
||||
wdog_wmcr = wdog2_base + WDOG_WMCR;
|
||||
break;
|
||||
case 2:
|
||||
ccm_ccgr = pm_info->ccm_base.vbase + CCGR_WDOG3;
|
||||
wdog_wmcr = wdog3_base + WDOG_WMCR;
|
||||
break;
|
||||
case 3:
|
||||
ccm_ccgr = pm_info->ccm_base.vbase + CCGR_WDOG4;
|
||||
wdog_wmcr = wdog4_base + WDOG_WMCR;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save WDOG clock states */
|
||||
val_root = readl_relaxed(ccm_root);
|
||||
val_ccgr = readl_relaxed(ccm_ccgr);
|
||||
|
||||
/* Turn on WDOG root and ccgr clock */
|
||||
writel_relaxed(val_root | BIT(28), ccm_root);
|
||||
writel_relaxed(0x3, ccm_ccgr);
|
||||
|
||||
/* Clear powerdown bit */
|
||||
writew_relaxed(0, wdog_wmcr);
|
||||
|
||||
/* Restore WDOG clock states */
|
||||
writel_relaxed(val_ccgr, ccm_ccgr);
|
||||
writel_relaxed(val_root, ccm_root);
|
||||
}
|
||||
|
||||
static void imx7_disable_wdog_powerdown(void)
|
||||
{
|
||||
disable_wdog_powerdown_single(0);
|
||||
disable_wdog_powerdown_single(1);
|
||||
disable_wdog_powerdown_single(2);
|
||||
disable_wdog_powerdown_single(3);
|
||||
}
|
||||
|
||||
static void imx7_gpio_save(void)
|
||||
{
|
||||
u32 i;
|
||||
|
@ -719,9 +790,121 @@ static int imx7_pm_is_resume_from_lpsr(void)
|
|||
return readl_relaxed(lpsr_base);
|
||||
}
|
||||
|
||||
static int imx7_pm_enter(suspend_state_t state)
|
||||
static void imx7_lpsr_tweaks_enable(bool lpsr_enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* To get GPIO1/2 LPSR wakeup work,
|
||||
* set bit 7 of SNVS register 0x48. Unfortunately, it brings us
|
||||
* a side effect, that is setting TOP (Turn off System Power) bit
|
||||
* of SNVS LPCR register results in an immediate reset instead of
|
||||
* power-off. To work around the issue, we only enable this when
|
||||
* entering LPSR sleep state.
|
||||
*/
|
||||
val = readl_relaxed(pm_info->snvs_base.vbase + 0x48);
|
||||
val &= ~BIT(7);
|
||||
val |= lpsr_enable ? BIT(7) : 0;
|
||||
writel_relaxed(val, pm_info->snvs_base.vbase + 0x48);
|
||||
|
||||
/* Key press length, shorter keypress in LPSR sleep */
|
||||
val = readl_relaxed(pm_info->snvs_base.vbase + SNVS_LPCR);
|
||||
val &= ~SNVS_LPCR_ON_TIME_MASK;
|
||||
val |= (lpsr_enable ? SNVS_LPCR_ON_TIME_50MS : SNVS_LPCR_ON_TIME_500MS)
|
||||
<< SNVS_LPCR_ON_TIME_SHIFT;
|
||||
writel_relaxed(val, pm_info->snvs_base.vbase + SNVS_LPCR);
|
||||
}
|
||||
|
||||
static void imx7_pm_do_suspend(bool lpsr)
|
||||
{
|
||||
unsigned int console_saved_reg[10] = {0};
|
||||
|
||||
imx_anatop_pre_suspend();
|
||||
imx_gpcv2_pre_suspend(true);
|
||||
if (imx_gpcv2_is_mf_mix_off()) {
|
||||
/*
|
||||
* per design requirement, EXSC for PCIe/EIM/PXP
|
||||
* will need clock to recover RDC setting on
|
||||
* resume, so enable PCIe/EIM LPCG for RDC
|
||||
* recovery when M/F mix off
|
||||
*/
|
||||
writel_relaxed(0x3, pm_info->ccm_base.vbase +
|
||||
CCM_EIM_LPCG);
|
||||
writel_relaxed(0x3, pm_info->ccm_base.vbase +
|
||||
CCM_PXP_LPCG);
|
||||
writel_relaxed(0x3, pm_info->ccm_base.vbase +
|
||||
CCM_PCIE_LPCG);
|
||||
/* stop m4 if mix will also be shutdown */
|
||||
if (imx_src_is_m4_enabled() && imx_mu_is_m4_in_stop()) {
|
||||
writel(M4_RCR_HALT,
|
||||
pm_info->src_base.vbase + M4RCR);
|
||||
imx_gpcv2_enable_wakeup_for_m4();
|
||||
}
|
||||
imx7_console_save(console_saved_reg);
|
||||
memcpy(ocram_saved_in_ddr, ocram_base, ocram_size);
|
||||
if (lpsr) {
|
||||
imx7_lpsr_tweaks_enable(true);
|
||||
imx7_pm_set_lpsr_resume_addr(pm_info->resume_addr);
|
||||
imx7_console_io_save();
|
||||
memcpy(lpm_ocram_saved_in_ddr, lpm_ocram_base,
|
||||
lpm_ocram_size);
|
||||
imx7_iomuxc_gpr_save();
|
||||
imx7_ccm_save();
|
||||
imx7_gpt_save();
|
||||
imx7_sys_counter_save();
|
||||
imx7_gpio_save();
|
||||
}
|
||||
}
|
||||
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7_suspend_finish);
|
||||
|
||||
if (imx7_pm_is_resume_from_lpsr()) {
|
||||
imx7_console_io_restore();
|
||||
memcpy(lpm_ocram_base, lpm_ocram_saved_in_ddr,
|
||||
lpm_ocram_size);
|
||||
imx7_iomuxc_gpr_restore();
|
||||
imx7_ccm_restore();
|
||||
imx7_gpt_restore();
|
||||
imx7_sys_counter_restore();
|
||||
imx7_gpio_restore();
|
||||
imx7d_enable_rcosc();
|
||||
imx7_disable_wdog_powerdown();
|
||||
imx7_lpsr_tweaks_enable(false);
|
||||
}
|
||||
if (imx_gpcv2_is_mf_mix_off() ||
|
||||
imx7_pm_is_resume_from_lpsr()) {
|
||||
writel_relaxed(0x0, pm_info->ccm_base.vbase +
|
||||
CCM_EIM_LPCG);
|
||||
writel_relaxed(0x0, pm_info->ccm_base.vbase +
|
||||
CCM_PXP_LPCG);
|
||||
writel_relaxed(0x0, pm_info->ccm_base.vbase +
|
||||
CCM_PCIE_LPCG);
|
||||
memcpy(ocram_base, ocram_saved_in_ddr, ocram_size);
|
||||
imx7_console_restore(console_saved_reg);
|
||||
if (imx_src_is_m4_enabled() && imx_mu_is_m4_in_stop()) {
|
||||
imx_gpcv2_disable_wakeup_for_m4();
|
||||
/* restore M4 image */
|
||||
memcpy(lpm_m4tcm_base,
|
||||
lpm_m4tcm_saved_in_ddr, SZ_32K);
|
||||
/* kick m4 to enable */
|
||||
writel(M4_RCR_GO,
|
||||
pm_info->src_base.vbase + M4RCR);
|
||||
/* offset high bus count for m4 image */
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
/* restore M4 to run mode */
|
||||
imx_mu_set_m4_run_mode();
|
||||
/* gpc wakeup */
|
||||
}
|
||||
}
|
||||
/* clear LPSR resume address */
|
||||
imx7_pm_set_lpsr_resume_addr(0);
|
||||
imx_anatop_post_resume();
|
||||
imx_gpcv2_post_resume();
|
||||
}
|
||||
|
||||
static int imx7_pm_enter(suspend_state_t state)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!iram_tlb_base_addr) {
|
||||
|
@ -752,98 +935,10 @@ static int imx7_pm_enter(suspend_state_t state)
|
|||
|
||||
switch (state) {
|
||||
case PM_SUSPEND_STANDBY:
|
||||
imx_anatop_pre_suspend();
|
||||
imx_gpcv2_pre_suspend(false);
|
||||
|
||||
/* Zzz ... */
|
||||
if (psci_ops.cpu_suspend)
|
||||
cpu_suspend(1, imx7_suspend_finish);
|
||||
else
|
||||
imx7_suspend_in_ocram_fn(suspend_ocram_base);
|
||||
|
||||
imx_anatop_post_resume();
|
||||
imx_gpcv2_post_resume();
|
||||
imx7_pm_do_suspend(false);
|
||||
break;
|
||||
case PM_SUSPEND_MEM:
|
||||
imx_anatop_pre_suspend();
|
||||
imx_gpcv2_pre_suspend(true);
|
||||
if (imx_gpcv2_is_mf_mix_off()) {
|
||||
/*
|
||||
* per design requirement, EXSC for PCIe/EIM/PXP
|
||||
* will need clock to recover RDC setting on
|
||||
* resume, so enable PCIe/EIM LPCG for RDC
|
||||
* recovery when M/F mix off
|
||||
*/
|
||||
writel_relaxed(0x3, pm_info->ccm_base.vbase +
|
||||
CCM_EIM_LPCG);
|
||||
writel_relaxed(0x3, pm_info->ccm_base.vbase +
|
||||
CCM_PXP_LPCG);
|
||||
writel_relaxed(0x3, pm_info->ccm_base.vbase +
|
||||
CCM_PCIE_LPCG);
|
||||
/* stop m4 if mix will also be shutdown */
|
||||
if (imx_src_is_m4_enabled() && imx_mu_is_m4_in_stop()) {
|
||||
writel(M4_RCR_HALT,
|
||||
pm_info->src_base.vbase + M4RCR);
|
||||
imx_gpcv2_enable_wakeup_for_m4();
|
||||
}
|
||||
imx7_console_save(console_saved_reg);
|
||||
memcpy(ocram_saved_in_ddr, ocram_base, ocram_size);
|
||||
if (lpsr_enabled) {
|
||||
imx7_pm_set_lpsr_resume_addr(pm_info->resume_addr);
|
||||
imx7_console_io_save();
|
||||
memcpy(lpm_ocram_saved_in_ddr, lpm_ocram_base,
|
||||
lpm_ocram_size);
|
||||
imx7_iomuxc_gpr_save();
|
||||
imx7_ccm_save();
|
||||
imx7_gpt_save();
|
||||
imx7_sys_counter_save();
|
||||
imx7_gpio_save();
|
||||
}
|
||||
}
|
||||
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7_suspend_finish);
|
||||
|
||||
if (imx7_pm_is_resume_from_lpsr()) {
|
||||
imx7_console_io_restore();
|
||||
memcpy(lpm_ocram_base, lpm_ocram_saved_in_ddr,
|
||||
lpm_ocram_size);
|
||||
imx7_iomuxc_gpr_restore();
|
||||
imx7_ccm_restore();
|
||||
imx7_gpt_restore();
|
||||
imx7_sys_counter_restore();
|
||||
imx7_gpio_restore();
|
||||
imx7d_enable_rcosc();
|
||||
}
|
||||
if (imx_gpcv2_is_mf_mix_off() ||
|
||||
imx7_pm_is_resume_from_lpsr()) {
|
||||
writel_relaxed(0x0, pm_info->ccm_base.vbase +
|
||||
CCM_EIM_LPCG);
|
||||
writel_relaxed(0x0, pm_info->ccm_base.vbase +
|
||||
CCM_PXP_LPCG);
|
||||
writel_relaxed(0x0, pm_info->ccm_base.vbase +
|
||||
CCM_PCIE_LPCG);
|
||||
memcpy(ocram_base, ocram_saved_in_ddr, ocram_size);
|
||||
imx7_console_restore(console_saved_reg);
|
||||
if (imx_src_is_m4_enabled() && imx_mu_is_m4_in_stop()) {
|
||||
imx_gpcv2_disable_wakeup_for_m4();
|
||||
/* restore M4 image */
|
||||
memcpy(lpm_m4tcm_base,
|
||||
lpm_m4tcm_saved_in_ddr, SZ_32K);
|
||||
/* kick m4 to enable */
|
||||
writel(M4_RCR_GO,
|
||||
pm_info->src_base.vbase + M4RCR);
|
||||
/* offset high bus count for m4 image */
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
/* restore M4 to run mode */
|
||||
imx_mu_set_m4_run_mode();
|
||||
/* gpc wakeup */
|
||||
}
|
||||
}
|
||||
/* clear LPSR resume address */
|
||||
imx7_pm_set_lpsr_resume_addr(0);
|
||||
imx_anatop_post_resume();
|
||||
imx_gpcv2_post_resume();
|
||||
imx7_pm_do_suspend(true);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -1159,15 +1254,12 @@ void __init imx7d_pm_init(void)
|
|||
/* save M4 Image to DDR */
|
||||
memcpy(lpm_m4tcm_saved_in_ddr, lpm_m4tcm_base, SZ_32K);
|
||||
}
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,lpm-sram");
|
||||
if (of_get_property(np, "fsl,enable-lpsr", NULL))
|
||||
lpsr_enabled = true;
|
||||
|
||||
if (psci_ops.cpu_suspend)
|
||||
lpsr_enabled = false;
|
||||
|
||||
if (lpsr_enabled) {
|
||||
pr_info("LPSR mode enabled, DSM will go into LPSR mode!\n");
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,lpm-sram");
|
||||
lpm_ocram_base = of_iomap(np, 0);
|
||||
WARN_ON(!lpm_ocram_base);
|
||||
WARN_ON(of_address_to_resource(np, 0, &res));
|
||||
|
@ -1198,6 +1290,30 @@ void __init imx7d_pm_init(void)
|
|||
if (np)
|
||||
gpio1_base = of_iomap(np, 0);
|
||||
WARN_ON(!gpio1_base);
|
||||
|
||||
np = of_find_node_by_path(
|
||||
"/soc/aips-bus@30000000/wdog@30280000");
|
||||
if (np)
|
||||
wdog1_base = of_iomap(np, 0);
|
||||
WARN_ON(!wdog1_base);
|
||||
|
||||
np = of_find_node_by_path(
|
||||
"/soc/aips-bus@30000000/wdog@30290000");
|
||||
if (np)
|
||||
wdog2_base = of_iomap(np, 0);
|
||||
WARN_ON(!wdog2_base);
|
||||
|
||||
np = of_find_node_by_path(
|
||||
"/soc/aips-bus@30000000/wdog@302a0000");
|
||||
if (np)
|
||||
wdog3_base = of_iomap(np, 0);
|
||||
WARN_ON(!wdog3_base);
|
||||
|
||||
np = of_find_node_by_path(
|
||||
"/soc/aips-bus@30000000/wdog@302b0000");
|
||||
if (np)
|
||||
wdog4_base = of_iomap(np, 0);
|
||||
WARN_ON(!wdog4_base);
|
||||
}
|
||||
|
||||
np = of_find_node_by_path(
|
||||
|
|
|
@ -869,6 +869,12 @@ config GPIO_GW_PLD
|
|||
Say yes here to provide access to the Gateworks I2C PLD GPIO
|
||||
Expander. This is used at least on the Cambria GW2358-4.
|
||||
|
||||
config GPIO_BD7181X
|
||||
tristate "BD7181X GPO"
|
||||
depends on MFD_BD7181X
|
||||
help
|
||||
Say yes here to access the GPO signals of bd71815/bd71817 chip from ROHM.
|
||||
|
||||
config GPIO_MAX7300
|
||||
tristate "Maxim MAX7300 GPIO expander"
|
||||
select GPIO_MAX730X
|
||||
|
|
|
@ -38,6 +38,7 @@ obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o
|
|||
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
|
||||
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
|
||||
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
|
||||
obj-$(CONFIG_GPIO_BD7181X) += gpio-bd7181x.o
|
||||
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
|
||||
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
|
||||
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* gpio-bd7181x.c
|
||||
* @file Access to GPOs on ROHM BD7181XMWV chip
|
||||
*
|
||||
* Copyright 2014 Embest Technology Co. Ltd. Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#define DEBUG
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/mfd/bd7181x.h>
|
||||
|
||||
/** @brief bd7181x gpio chip core data */
|
||||
static struct gpio_chip bd7181xgpo_chip;
|
||||
|
||||
/** @brief get gpo output value
|
||||
* @param chip pointer to core data
|
||||
* @param offset gpo number, start from 0
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181xgpo_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct bd7181x *bd7181x = dev_get_drvdata(chip->parent);
|
||||
int ret = 0;
|
||||
|
||||
ret = bd7181x_reg_read(bd7181x, BD7181X_REG_GPO);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret >> offset) & 1;
|
||||
}
|
||||
|
||||
/** @brief set gpo direction as output
|
||||
* @param chip pointer to core data
|
||||
* @param offset gpo number, start from 0
|
||||
* @param value output value when set direction out
|
||||
* @retval 0 success
|
||||
*/
|
||||
static int bd7181xgpo_direction_out(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
/* This only drives GPOs, and can't change direction */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief set gpo output value
|
||||
* @param chip pointer to core data
|
||||
* @param offset gpo number, start from 0
|
||||
* @param value output value, not zero as high level, 0 as low level
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static void bd7181xgpo_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct bd7181x *bd7181x = dev_get_drvdata(chip->parent);
|
||||
int ret;
|
||||
u8 gpoctl;
|
||||
|
||||
ret = bd7181x_reg_read(bd7181x, BD7181X_REG_GPO);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (value)
|
||||
gpoctl = ret | (1 << offset);
|
||||
else
|
||||
gpoctl = ret & ~(1 << offset);
|
||||
|
||||
bd7181x_reg_write(bd7181x, BD7181X_REG_GPO, gpoctl);
|
||||
}
|
||||
|
||||
/** @brief bd7181x gpio chip core data */
|
||||
static struct gpio_chip bd7181xgpo_chip = {
|
||||
.label = "bd7181x", ///< gpio chip name
|
||||
.owner = THIS_MODULE,
|
||||
.get = bd7181xgpo_get,
|
||||
.direction_output = bd7181xgpo_direction_out,
|
||||
.set = bd7181xgpo_set,
|
||||
.can_sleep = 1,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
#ifdef CONFIG_OF
|
||||
/** @brief retrive gpo platform data from device tree
|
||||
* @param pdev platfrom device pointer
|
||||
* @return pointer to platform data
|
||||
* @retval NULL error
|
||||
*/
|
||||
static struct bd7181x_gpo_plat_data *of_gpio_bd7181x(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct bd7181x_gpo_plat_data *platform_data;
|
||||
struct device_node *np, *gpio_np;
|
||||
|
||||
platform_data = devm_kzalloc(&pdev->dev, sizeof(*platform_data), GFP_KERNEL);
|
||||
if (!platform_data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
np = of_node_get(pdev->dev.parent->of_node);
|
||||
gpio_np = of_find_node_by_name(np, "gpo");
|
||||
if (!gpio_np) {
|
||||
dev_err(&pdev->dev, "gpio node not found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdev->dev.of_node = gpio_np;
|
||||
|
||||
if (of_property_read_u32(gpio_np, "rohm,mode", &platform_data->mode)) {
|
||||
platform_data->mode = -1;
|
||||
}
|
||||
|
||||
return platform_data;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @brief probe bd7181x gpo device
|
||||
* @param pdev platfrom device pointer
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int gpo_bd7181x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bd7181x_gpo_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct device *mfd_dev = pdev->dev.parent;
|
||||
struct bd7181x *bd7181x = dev_get_drvdata(mfd_dev);
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
pdata = of_gpio_bd7181x(pdev);
|
||||
#endif
|
||||
if (pdata && pdata->gpio_base > 0)
|
||||
bd7181xgpo_chip.base = pdata->gpio_base;
|
||||
else
|
||||
bd7181xgpo_chip.base = -1;
|
||||
|
||||
bd7181xgpo_chip.ngpio = 2; /* bd71815/bd71817 have 2 GPO */
|
||||
|
||||
bd7181xgpo_chip.parent = &pdev->dev;
|
||||
|
||||
ret = gpiochip_add(&bd7181xgpo_chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||
bd7181xgpo_chip.ngpio = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata && pdata->mode != -1UL) {
|
||||
bd7181x_update_bits(bd7181x, BD7181X_REG_GPO, 0x70, pdata->mode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @brief remove bd7181x gpo device
|
||||
* @param pdev platfrom device pointer
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int gpo_bd7181x_remove(struct platform_device *pdev)
|
||||
{
|
||||
gpiochip_remove(&bd7181xgpo_chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note: this hardware lives inside an I2C-based multi-function device. */
|
||||
MODULE_ALIAS("platform:bd7181x-gpo");
|
||||
|
||||
/** @brief bd7181x gpo driver core data */
|
||||
static struct platform_driver gpo_bd7181x_driver = {
|
||||
.driver = {
|
||||
.name = "bd7181x-gpo",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = gpo_bd7181x_probe,
|
||||
.remove = gpo_bd7181x_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(gpo_bd7181x_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
|
||||
MODULE_DESCRIPTION("GPO interface for BD71815/BD71817");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1424,6 +1424,16 @@ config SENSORS_SIS5595
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called sis5595.
|
||||
|
||||
config SENSORS_SY7636A
|
||||
tristate "Silergy SY7636A"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the thermistor readout of
|
||||
the Silergy SY7636A PMIC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called sy7636a-hwmon.
|
||||
|
||||
config SENSORS_DME1737
|
||||
tristate "SMSC DME1737, SCH311x and compatibles"
|
||||
depends on I2C && !PPC
|
||||
|
|
|
@ -159,6 +159,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
|
|||
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
||||
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
|
||||
obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
|
||||
obj-$(CONFIG_SENSORS_TC74) += tc74.o
|
||||
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Functions to access SY3686A power management chip temperature
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; 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/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/mfd/sy7636a.h>
|
||||
|
||||
struct sy7636a_data {
|
||||
struct sy7636a *sy7636a;
|
||||
struct device *hwmon_dev;
|
||||
};
|
||||
|
||||
static ssize_t show_temp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned int reg_val;
|
||||
signed char temp;
|
||||
int ret;
|
||||
struct sy7636a_data *data = dev_get_drvdata(dev);
|
||||
|
||||
ret = regmap_read(data->sy7636a->regmap,
|
||||
SY7636A_REG_TERMISTOR_READOUT, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
temp = *((signed char*)®_val);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", temp);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp0, S_IRUGO, show_temp, NULL, 0);
|
||||
|
||||
static struct attribute *sy7636a_attrs[] = {
|
||||
&sensor_dev_attr_temp0.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(sy7636a);
|
||||
|
||||
static int sy7636a_sensor_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(pdev->dev.parent);
|
||||
struct sy7636a_data *data;
|
||||
int err;
|
||||
|
||||
if (!sy7636a)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct sy7636a_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->sy7636a = sy7636a;
|
||||
data->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
|
||||
"sy7636a_temperature", data, sy7636a_groups);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
dev_err(&pdev->dev, "Unable to register hwmon device, returned %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id sy7636a_sensor_id[] = {
|
||||
{ "sy7636a-temperature", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sy7636a_sensor_id);
|
||||
|
||||
static struct platform_driver sy7636a_sensor_driver = {
|
||||
.probe = sy7636a_sensor_probe,
|
||||
.id_table = sy7636a_sensor_id,
|
||||
.driver = {
|
||||
.name = "sy7636a-temperature",
|
||||
},
|
||||
};
|
||||
module_platform_driver(sy7636a_sensor_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>");
|
||||
MODULE_DESCRIPTION("Silergy SY7636A Sensor Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -289,6 +289,225 @@ config TOUCHSCREEN_CYTTSP4_SPI
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp4_spi.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
tristate "Parade TrueTouch Gen5 Touchscreen Driver"
|
||||
help
|
||||
Core driver for Parade TrueTouch(tm) Standard Product
|
||||
Geneartion5 touchscreen controllers.
|
||||
|
||||
Say Y here if you have a Parade Gen5 touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
bool "Enable Device Tree support"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5 && OF
|
||||
help
|
||||
Say Y here to enable support for device tree.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG
|
||||
bool "Enable debug output"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
help
|
||||
Say Y here to enable debug output for Parade TrueTouch(tm)
|
||||
Standard Product Generation5 drivers set.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_VDEBUG
|
||||
bool "Enable verbose debug output"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG
|
||||
help
|
||||
Say Y here to enable verbose debug output for Parade TrueTouch(tm)
|
||||
Standard Product Generation5 drivers set.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_I2C
|
||||
tristate "Parade TrueTouch Gen5 I2C"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
select I2C
|
||||
help
|
||||
Say Y here to enable I2C bus interface to Parade TrueTouch(tm)
|
||||
Standard Product Generation5 touchscreen controller.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5_i2c.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_SPI
|
||||
tristate "Parade TrueTouch Gen5 SPI"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
select SPI
|
||||
help
|
||||
Say Y here to enable SPI bus interface to Parade TrueTouch(tm)
|
||||
Standard Product Generation5 touchscreen controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5_spi.
|
||||
|
||||
choice
|
||||
bool "Parade TrueTouch Gen5 MultiTouch Protocol"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
default TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B
|
||||
help
|
||||
This option controls which MultiTouch protocol will be used to
|
||||
report the touch events.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A
|
||||
bool "Protocol A"
|
||||
help
|
||||
Select to enable MultiTouch touch reporting using protocol A
|
||||
on Parade TrueTouch(tm) Standard Product Generation4 touchscreen
|
||||
controller.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B
|
||||
bool "Protocol B"
|
||||
help
|
||||
Select to enable MultiTouch touch reporting using protocol B
|
||||
on Parade TrueTouch(tm) Standard Product Generation4 touchscreen
|
||||
controller.
|
||||
|
||||
endchoice
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON
|
||||
bool "Parade TrueTouch Gen5 MultiTouch CapSense Button"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
help
|
||||
Say Y here to enable CapSense reporting on Parade TrueTouch(tm)
|
||||
Standard Product Generation5 touchscreen controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY
|
||||
bool "Parade TrueTouch Gen5 Proximity"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
help
|
||||
Say Y here to enable proximity reporting on Parade TrueTouch(tm)
|
||||
Standard Product Generation5 touchscreen controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS
|
||||
tristate "Parade TrueTouch Gen5 MultiTouch Device Access"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
help
|
||||
Say Y here to enable Parade TrueTouch(tm) Standard Product
|
||||
Generation5 touchscreen controller device access module.
|
||||
|
||||
This modules adds an interface to access touchscreen
|
||||
controller using driver sysfs nodes.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5_device_access.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API
|
||||
bool "Enable Device Access kernel API"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS
|
||||
help
|
||||
Say Y here to enable Device access kernel API which provides
|
||||
access to Parade TrueTouch(tm) Standard Product Generation5
|
||||
touchscreen controller for other modules.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_TEST_DEVICE_ACCESS_API
|
||||
tristate "Simple Test module for Device Access kernel API"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API
|
||||
help
|
||||
Say Y here to enable test module for Device access kernel API.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5_test_device_access_api.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER
|
||||
tristate "Parade TrueTouch Gen5 MultiTouch Loader"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
help
|
||||
Say Y here to enable Parade TrueTouch(tm) Standard Product
|
||||
Generation5 touchscreen controller FW Loader module.
|
||||
|
||||
This module enables support for Firmware upgrade.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5_loader.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
|
||||
bool "FW upgrade from header file"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER
|
||||
help
|
||||
Say Y here to include Parade TrueTouch(tm) Standard Product
|
||||
Generation5 device Firmware into driver.
|
||||
|
||||
Need proper header file for this.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
|
||||
bool "FW upgrade from binary file"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER
|
||||
help
|
||||
Say Y here to include Parade TrueTouch(tm) Standard Product
|
||||
Generation5 device Firmware into kernel as binary blob.
|
||||
|
||||
This should be enabled for manual FW upgrade support.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
|
||||
bool "TT Configuration upgrade from header file"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER
|
||||
help
|
||||
Say Y here to include Parade TrueTouch(tm) Standard Product
|
||||
Generation5 device TrueTouch Configuration into kernel itself.
|
||||
|
||||
Need proper header file for this.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
|
||||
bool "TT Configuration upgrade via SysFs"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER
|
||||
help
|
||||
Say Y here to provide a SysFs interface to upgrade TrueTouch
|
||||
Configuration with a binary configuration file.
|
||||
|
||||
Need proper binary version of config file for this
|
||||
feature.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG_MDL
|
||||
tristate "Parade TrueTouch Gen5 MultiTouch Debug Module"
|
||||
depends on TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
help
|
||||
Say Y here to enable Parade TrueTouch(tm) Standard Product
|
||||
Generation5 Debug module.
|
||||
|
||||
This module adds support for verbose printing touch
|
||||
information.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp5_debug.
|
||||
|
||||
|
||||
config TOUCHSCREEN_DA9034
|
||||
tristate "Touchscreen support for Dialog Semiconductor DA9034"
|
||||
depends on PMIC_DA903X
|
||||
|
|
|
@ -113,4 +113,51 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) += cyttsp5.o
|
||||
cyttsp5-y := cyttsp5_core.o cyttsp5_mt_common.o
|
||||
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A) += cyttsp5_mta.o
|
||||
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B) += cyttsp5_mtb.o
|
||||
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON) += cyttsp5_btn.o
|
||||
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY) += cyttsp5_proximity.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT) += cyttsp5_devtree.o
|
||||
ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5
|
||||
obj-y += cyttsp5_platform.o
|
||||
endif
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_I2C) += cyttsp5_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_SPI) += cyttsp5_spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG_MDL) += cyttsp5_debug.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER) += cyttsp5_loader.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS) += cyttsp5_device_access.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_TEST_DEVICE_ACCESS_API) += cyttsp5_test_device_access_api.o
|
||||
|
||||
ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG),y)
|
||||
CFLAGS_cyttsp5_core.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_i2c.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_spi.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_mta.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_mtb.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_mt_common.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_btn.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_proximity.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_device_access.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_loader.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_debug.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_devtree.o += -DDEBUG
|
||||
CFLAGS_cyttsp5_platform.o += -DDEBUG
|
||||
endif
|
||||
ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_VDEBUG),y)
|
||||
CFLAGS_cyttsp5_core.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_i2c.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_spi.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_mta.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_mtb.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_mt_common.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_btn.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_proximity.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_device_access.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_loader.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_debug.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_devtree.o += -DVERBOSE_DEBUG
|
||||
CFLAGS_cyttsp5_platform.o += -DVERBOSE_DEBUG
|
||||
endif
|
||||
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* cyttsp5_btn.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 CapSense Reports Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
#define CYTTSP5_BTN_NAME "cyttsp5_btn"
|
||||
|
||||
static inline void cyttsp5_btn_key_action(struct cyttsp5_btn_data *bd,
|
||||
int btn_no, int btn_state)
|
||||
{
|
||||
struct device *dev = bd->dev;
|
||||
struct cyttsp5_sysinfo *si = bd->si;
|
||||
|
||||
if (!si->btn[btn_no].enabled ||
|
||||
si->btn[btn_no].state == btn_state)
|
||||
return;
|
||||
|
||||
si->btn[btn_no].state = btn_state;
|
||||
input_report_key(bd->input, si->btn[btn_no].key_code, btn_state);
|
||||
input_sync(bd->input);
|
||||
|
||||
dev_dbg(dev, "%s: btn=%d key_code=%d %s\n", __func__,
|
||||
btn_no, si->btn[btn_no].key_code,
|
||||
btn_state == CY_BTN_PRESSED ?
|
||||
"PRESSED" : "RELEASED");
|
||||
}
|
||||
|
||||
static void cyttsp5_get_btn_touches(struct cyttsp5_btn_data *bd)
|
||||
{
|
||||
struct cyttsp5_sysinfo *si = bd->si;
|
||||
int num_btns = si->num_btns;
|
||||
int cur_btn;
|
||||
int cur_btn_state;
|
||||
|
||||
for (cur_btn = 0; cur_btn < num_btns; cur_btn++) {
|
||||
/* Get current button state */
|
||||
cur_btn_state = (si->xy_data[0] >> (cur_btn * CY_BITS_PER_BTN))
|
||||
& CY_NUM_BTN_EVENT_ID;
|
||||
|
||||
cyttsp5_btn_key_action(bd, cur_btn, cur_btn_state);
|
||||
}
|
||||
}
|
||||
|
||||
static void cyttsp5_btn_lift_all(struct cyttsp5_btn_data *bd)
|
||||
{
|
||||
struct cyttsp5_sysinfo *si = bd->si;
|
||||
int i;
|
||||
|
||||
if (!si || si->num_btns == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < si->num_btns; i++)
|
||||
cyttsp5_btn_key_action(bd, i, CY_BTN_RELEASED);
|
||||
}
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
static void cyttsp5_log_btn_data(struct cyttsp5_btn_data *bd)
|
||||
{
|
||||
struct device *dev = bd->dev;
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
u8 *pr_buf = cd->pr_buf;
|
||||
struct cyttsp5_sysinfo *si = bd->si;
|
||||
int cur;
|
||||
int value;
|
||||
|
||||
for (cur = 0; cur < si->num_btns; cur++) {
|
||||
pr_buf[0] = 0;
|
||||
if (si->xy_data[0] & (1 << cur))
|
||||
value = 1;
|
||||
else
|
||||
value = 0;
|
||||
snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "btn_rec[%d]=0x", cur);
|
||||
snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "%s%X (%02X)",
|
||||
pr_buf, value,
|
||||
le16_to_cpu(si->xy_data[1 + cur * 2]));
|
||||
|
||||
dev_vdbg(dev, "%s: %s\n", __func__, pr_buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* read xy_data for all current CapSense button touches */
|
||||
static int cyttsp5_xy_worker(struct cyttsp5_btn_data *bd)
|
||||
{
|
||||
struct cyttsp5_sysinfo *si = bd->si;
|
||||
|
||||
/* extract button press/release touch information */
|
||||
if (si->num_btns > 0) {
|
||||
cyttsp5_get_btn_touches(bd);
|
||||
#ifdef VERBOSE_DEBUG
|
||||
/* log button press/release touch information */
|
||||
cyttsp5_log_btn_data(bd);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_btn_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
int rc;
|
||||
|
||||
if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id)
|
||||
return 0;
|
||||
|
||||
/* core handles handshake */
|
||||
mutex_lock(&bd->btn_lock);
|
||||
rc = cyttsp5_xy_worker(bd);
|
||||
mutex_unlock(&bd->btn_lock);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_startup_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
|
||||
mutex_lock(&bd->btn_lock);
|
||||
cyttsp5_btn_lift_all(bd);
|
||||
mutex_unlock(&bd->btn_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_btn_suspend_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
|
||||
mutex_lock(&bd->btn_lock);
|
||||
cyttsp5_btn_lift_all(bd);
|
||||
bd->is_suspended = true;
|
||||
mutex_unlock(&bd->btn_lock);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_btn_resume_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
|
||||
pm_runtime_get(dev);
|
||||
|
||||
mutex_lock(&bd->btn_lock);
|
||||
bd->is_suspended = false;
|
||||
mutex_unlock(&bd->btn_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_btn_open(struct input_dev *input)
|
||||
{
|
||||
struct device *dev = input->dev.parent;
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
mutex_lock(&bd->btn_lock);
|
||||
bd->is_suspended = false;
|
||||
mutex_unlock(&bd->btn_lock);
|
||||
|
||||
dev_vdbg(dev, "%s: setup subscriptions\n", __func__);
|
||||
|
||||
/* set up touch call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_btn_attention, CY_MODE_OPERATIONAL);
|
||||
|
||||
/* set up startup call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_startup_attention, 0);
|
||||
|
||||
/* set up suspend call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_btn_suspend_attention, 0);
|
||||
|
||||
/* set up resume call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_btn_resume_attention, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cyttsp5_btn_close(struct input_dev *input)
|
||||
{
|
||||
struct device *dev = input->dev.parent;
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_btn_attention, CY_MODE_OPERATIONAL);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_startup_attention, 0);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_btn_suspend_attention, 0);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_btn_resume_attention, 0);
|
||||
|
||||
mutex_lock(&bd->btn_lock);
|
||||
if (!bd->is_suspended) {
|
||||
pm_runtime_put(dev);
|
||||
bd->is_suspended = true;
|
||||
}
|
||||
mutex_unlock(&bd->btn_lock);
|
||||
}
|
||||
|
||||
static int cyttsp5_setup_input_device(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
|
||||
__set_bit(EV_KEY, bd->input->evbit);
|
||||
dev_vdbg(dev, "%s: Number of buttons %d\n", __func__, bd->si->num_btns);
|
||||
for (i = 0; i < bd->si->num_btns; i++) {
|
||||
dev_vdbg(dev, "%s: btn:%d keycode:%d\n",
|
||||
__func__, i, bd->si->btn[i].key_code);
|
||||
__set_bit(bd->si->btn[i].key_code, bd->input->keybit);
|
||||
}
|
||||
|
||||
rc = input_register_device(bd->input);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: Error, failed register input device r=%d\n",
|
||||
__func__, rc);
|
||||
else
|
||||
bd->input_device_registered = true;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_setup_input_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
int rc;
|
||||
|
||||
bd->si = _cyttsp5_request_sysinfo(dev);
|
||||
if (!bd->si)
|
||||
return -1;
|
||||
|
||||
rc = cyttsp5_setup_input_device(dev);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
|
||||
cyttsp5_setup_input_attention, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_btn_probe(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct cyttsp5_btn_platform_data *btn_pdata;
|
||||
int rc = 0;
|
||||
|
||||
if (!pdata || !pdata->btn_pdata) {
|
||||
dev_err(dev, "%s: Missing platform data\n", __func__);
|
||||
rc = -ENODEV;
|
||||
goto error_no_pdata;
|
||||
}
|
||||
btn_pdata = pdata->btn_pdata;
|
||||
|
||||
mutex_init(&bd->btn_lock);
|
||||
bd->dev = dev;
|
||||
bd->pdata = btn_pdata;
|
||||
|
||||
/* Create the input device and register it. */
|
||||
dev_vdbg(dev, "%s: Create the input device and register it\n",
|
||||
__func__);
|
||||
bd->input = input_allocate_device();
|
||||
if (!bd->input) {
|
||||
dev_err(dev, "%s: Error, failed to allocate input device\n",
|
||||
__func__);
|
||||
rc = -ENODEV;
|
||||
goto error_alloc_failed;
|
||||
}
|
||||
|
||||
if (bd->pdata->inp_dev_name)
|
||||
bd->input->name = bd->pdata->inp_dev_name;
|
||||
else
|
||||
bd->input->name = CYTTSP5_BTN_NAME;
|
||||
scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev),
|
||||
cd->phys_num++);
|
||||
bd->input->phys = bd->phys;
|
||||
bd->input->dev.parent = bd->dev;
|
||||
bd->input->open = cyttsp5_btn_open;
|
||||
bd->input->close = cyttsp5_btn_close;
|
||||
input_set_drvdata(bd->input, bd);
|
||||
|
||||
/* get sysinfo */
|
||||
bd->si = _cyttsp5_request_sysinfo(dev);
|
||||
|
||||
if (bd->si) {
|
||||
rc = cyttsp5_setup_input_device(dev);
|
||||
if (rc)
|
||||
goto error_init_input;
|
||||
} else {
|
||||
dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
|
||||
__func__, bd->si);
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_init_input:
|
||||
input_free_device(bd->input);
|
||||
error_alloc_failed:
|
||||
error_no_pdata:
|
||||
dev_err(dev, "%s failed.\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_btn_release(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_btn_data *bd = &cd->bd;
|
||||
|
||||
if (bd->input_device_registered) {
|
||||
input_unregister_device(bd->input);
|
||||
} else {
|
||||
input_free_device(bd->input);
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* cyttsp5_debug.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Debug Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
#define CYTTSP5_DEBUG_NAME "cyttsp5_debug"
|
||||
|
||||
struct cyttsp5_debug_data {
|
||||
struct device *dev;
|
||||
struct cyttsp5_sysinfo *si;
|
||||
uint32_t interrupt_count;
|
||||
uint32_t formated_output;
|
||||
struct mutex sysfs_lock;
|
||||
u8 pr_buf[CY_MAX_PRBUF_SIZE];
|
||||
};
|
||||
|
||||
static struct cyttsp5_core_commands *cmd;
|
||||
|
||||
static struct cyttsp5_module debug_module;
|
||||
|
||||
static inline struct cyttsp5_debug_data *cyttsp5_get_debug_data(
|
||||
struct device *dev)
|
||||
{
|
||||
return cyttsp5_get_module_data(dev, &debug_module);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function provide output of combined xy_mode and xy_data.
|
||||
* Required by TTHE.
|
||||
*/
|
||||
static void cyttsp5_pr_buf_op_mode(struct cyttsp5_debug_data *dd, u8 *pr_buf,
|
||||
struct cyttsp5_sysinfo *si, u8 cur_touch)
|
||||
{
|
||||
const char fmt[] = "%02X ";
|
||||
int max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
|
||||
u8 report_id = si->xy_mode[2];
|
||||
int header_size = 0;
|
||||
int report_size = 0;
|
||||
int total_size = 0;
|
||||
int i, k;
|
||||
|
||||
if (report_id == si->desc.tch_report_id) {
|
||||
header_size = si->desc.tch_header_size;
|
||||
report_size = cur_touch * si->desc.tch_record_size;
|
||||
} else if (report_id == si->desc.btn_report_id) {
|
||||
header_size = BTN_INPUT_HEADER_SIZE;
|
||||
report_size = BTN_REPORT_SIZE;
|
||||
}
|
||||
total_size = header_size + report_size;
|
||||
|
||||
pr_buf[0] = 0;
|
||||
for (i = k = 0; i < header_size && i < max; i++, k += 3)
|
||||
scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]);
|
||||
|
||||
for (i = 0; i < report_size && i < max; i++, k += 3)
|
||||
scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_data[i]);
|
||||
|
||||
pr_info("%s=%s%s\n", "cyttsp5_OpModeData", pr_buf,
|
||||
total_size <= max ? "" : CY_PR_TRUNCATED);
|
||||
}
|
||||
|
||||
static void cyttsp5_debug_print(struct device *dev, u8 *pr_buf, u8 *sptr,
|
||||
int size, const char *data_name)
|
||||
{
|
||||
int i, j;
|
||||
int elem_size = sizeof("XX ") - 1;
|
||||
int max = (CY_MAX_PRBUF_SIZE - 1) / elem_size;
|
||||
int limit = size < max ? size : max;
|
||||
|
||||
if (limit < 0)
|
||||
limit = 0;
|
||||
|
||||
pr_buf[0] = 0;
|
||||
for (i = j = 0; i < limit; i++, j += elem_size)
|
||||
scnprintf(pr_buf + j, CY_MAX_PRBUF_SIZE - j, "%02X ", sptr[i]);
|
||||
|
||||
if (size)
|
||||
pr_info("%s[0..%d]=%s%s\n", data_name, size - 1, pr_buf,
|
||||
size <= max ? "" : CY_PR_TRUNCATED);
|
||||
else
|
||||
pr_info("%s[]\n", data_name);
|
||||
}
|
||||
|
||||
static void cyttsp5_debug_formated(struct device *dev, u8 *pr_buf,
|
||||
struct cyttsp5_sysinfo *si, u8 num_cur_tch)
|
||||
{
|
||||
u8 report_id = si->xy_mode[2];
|
||||
int header_size = 0;
|
||||
int report_size = 0;
|
||||
u8 data_name[] = "touch[99]";
|
||||
int max_print_length = 20;
|
||||
int i;
|
||||
|
||||
if (report_id == si->desc.tch_report_id) {
|
||||
header_size = si->desc.tch_header_size;
|
||||
report_size = num_cur_tch * si->desc.tch_record_size;
|
||||
} else if (report_id == si->desc.btn_report_id) {
|
||||
header_size = BTN_INPUT_HEADER_SIZE;
|
||||
report_size = BTN_REPORT_SIZE;
|
||||
}
|
||||
|
||||
/* xy_mode */
|
||||
cyttsp5_debug_print(dev, pr_buf, si->xy_mode, header_size, "xy_mode");
|
||||
|
||||
/* xy_data */
|
||||
if (report_size > max_print_length) {
|
||||
pr_info("xy_data[0..%d]:\n", report_size);
|
||||
for (i = 0; i < report_size - max_print_length;
|
||||
i += max_print_length) {
|
||||
cyttsp5_debug_print(dev, pr_buf, si->xy_data + i,
|
||||
max_print_length, " ");
|
||||
}
|
||||
if (report_size - i)
|
||||
cyttsp5_debug_print(dev, pr_buf, si->xy_data + i,
|
||||
report_size - i, " ");
|
||||
} else {
|
||||
cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size,
|
||||
"xy_data");
|
||||
}
|
||||
|
||||
/* touches */
|
||||
if (report_id == si->desc.tch_report_id) {
|
||||
for (i = 0; i < num_cur_tch; i++) {
|
||||
scnprintf(data_name, sizeof(data_name) - 1,
|
||||
"touch[%u]", i);
|
||||
cyttsp5_debug_print(dev, pr_buf,
|
||||
si->xy_data + (i * si->desc.tch_record_size),
|
||||
si->desc.tch_record_size, data_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* buttons */
|
||||
if (report_id == si->desc.btn_report_id)
|
||||
cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size,
|
||||
"button");
|
||||
}
|
||||
|
||||
/* read xy_data for all touches for debug */
|
||||
static int cyttsp5_xy_worker(struct cyttsp5_debug_data *dd)
|
||||
{
|
||||
struct device *dev = dd->dev;
|
||||
struct cyttsp5_sysinfo *si = dd->si;
|
||||
u8 report_reg = si->xy_mode[TOUCH_COUNT_BYTE_OFFSET];
|
||||
u8 num_cur_tch = GET_NUM_TOUCHES(report_reg);
|
||||
uint32_t formated_output;
|
||||
|
||||
mutex_lock(&dd->sysfs_lock);
|
||||
dd->interrupt_count++;
|
||||
formated_output = dd->formated_output;
|
||||
mutex_unlock(&dd->sysfs_lock);
|
||||
|
||||
/* Interrupt */
|
||||
pr_info("Interrupt(%u)\n", dd->interrupt_count);
|
||||
|
||||
if (formated_output)
|
||||
cyttsp5_debug_formated(dev, dd->pr_buf, si, num_cur_tch);
|
||||
else
|
||||
/* print data for TTHE */
|
||||
cyttsp5_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch);
|
||||
|
||||
pr_info("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_debug_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
|
||||
struct cyttsp5_sysinfo *si = dd->si;
|
||||
u8 report_id = si->xy_mode[2];
|
||||
int rc = 0;
|
||||
|
||||
if (report_id != si->desc.tch_report_id
|
||||
&& report_id != si->desc.btn_report_id)
|
||||
return 0;
|
||||
|
||||
/* core handles handshake */
|
||||
rc = cyttsp5_xy_worker(dd);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t cyttsp5_interrupt_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
|
||||
int val;
|
||||
|
||||
mutex_lock(&dd->sysfs_lock);
|
||||
val = dd->interrupt_count;
|
||||
mutex_unlock(&dd->sysfs_lock);
|
||||
|
||||
return scnprintf(buf, CY_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t cyttsp5_interrupt_count_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
|
||||
|
||||
mutex_lock(&dd->sysfs_lock);
|
||||
dd->interrupt_count = 0;
|
||||
mutex_unlock(&dd->sysfs_lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(int_count, S_IRUSR | S_IWUSR,
|
||||
cyttsp5_interrupt_count_show, cyttsp5_interrupt_count_store);
|
||||
|
||||
static ssize_t cyttsp5_formated_output_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
|
||||
int val;
|
||||
|
||||
mutex_lock(&dd->sysfs_lock);
|
||||
val = dd->formated_output;
|
||||
mutex_unlock(&dd->sysfs_lock);
|
||||
|
||||
return scnprintf(buf, CY_MAX_PRBUF_SIZE,
|
||||
"Formated debug output: %x\n", val);
|
||||
}
|
||||
|
||||
static ssize_t cyttsp5_formated_output_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
|
||||
unsigned long value;
|
||||
int rc;
|
||||
|
||||
rc = kstrtoul(buf, 10, &value);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Invalid value\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Expecting only 0 or 1 */
|
||||
if (value != 0 && value != 1) {
|
||||
dev_err(dev, "%s: Invalid value %lu\n", __func__, value);
|
||||
return size;
|
||||
}
|
||||
|
||||
mutex_lock(&dd->sysfs_lock);
|
||||
dd->formated_output = value;
|
||||
mutex_unlock(&dd->sysfs_lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(formated_output, S_IRUSR | S_IWUSR,
|
||||
cyttsp5_formated_output_show, cyttsp5_formated_output_store);
|
||||
|
||||
static int cyttsp5_debug_probe(struct device *dev, void **data)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd;
|
||||
int rc;
|
||||
|
||||
/* get context and debug print buffers */
|
||||
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd) {
|
||||
rc = -ENOMEM;
|
||||
goto cyttsp5_debug_probe_alloc_failed;
|
||||
}
|
||||
|
||||
rc = device_create_file(dev, &dev_attr_int_count);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s: Error, could not create int_count\n",
|
||||
__func__);
|
||||
goto cyttsp5_debug_probe_create_int_count_failed;
|
||||
}
|
||||
|
||||
rc = device_create_file(dev, &dev_attr_formated_output);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s: Error, could not create formated_output\n",
|
||||
__func__);
|
||||
goto cyttsp5_debug_probe_create_formated_failed;
|
||||
}
|
||||
|
||||
mutex_init(&dd->sysfs_lock);
|
||||
dd->dev = dev;
|
||||
*data = dd;
|
||||
|
||||
dd->si = cmd->request_sysinfo(dev);
|
||||
if (!dd->si) {
|
||||
dev_err(dev, "%s: Fail get sysinfo pointer from core\n",
|
||||
__func__);
|
||||
rc = -ENODEV;
|
||||
goto cyttsp5_debug_probe_sysinfo_failed;
|
||||
}
|
||||
|
||||
rc = cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME,
|
||||
cyttsp5_debug_attention, CY_MODE_OPERATIONAL);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Error, could not subscribe attention cb\n",
|
||||
__func__);
|
||||
goto cyttsp5_debug_probe_subscribe_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cyttsp5_debug_probe_subscribe_failed:
|
||||
cyttsp5_debug_probe_sysinfo_failed:
|
||||
device_remove_file(dev, &dev_attr_formated_output);
|
||||
cyttsp5_debug_probe_create_formated_failed:
|
||||
device_remove_file(dev, &dev_attr_int_count);
|
||||
cyttsp5_debug_probe_create_int_count_failed:
|
||||
kfree(dd);
|
||||
cyttsp5_debug_probe_alloc_failed:
|
||||
dev_err(dev, "%s failed.\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void cyttsp5_debug_release(struct device *dev, void *data)
|
||||
{
|
||||
struct cyttsp5_debug_data *dd = data;
|
||||
int rc;
|
||||
|
||||
rc = cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME,
|
||||
cyttsp5_debug_attention, CY_MODE_OPERATIONAL);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Error, could not un-subscribe attention\n",
|
||||
__func__);
|
||||
goto cyttsp5_debug_release_exit;
|
||||
}
|
||||
|
||||
cyttsp5_debug_release_exit:
|
||||
device_remove_file(dev, &dev_attr_formated_output);
|
||||
device_remove_file(dev, &dev_attr_int_count);
|
||||
kfree(dd);
|
||||
}
|
||||
|
||||
static struct cyttsp5_module debug_module = {
|
||||
.name = CYTTSP5_DEBUG_NAME,
|
||||
.probe = cyttsp5_debug_probe,
|
||||
.release = cyttsp5_debug_release,
|
||||
};
|
||||
|
||||
static int __init cyttsp5_debug_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
cmd = cyttsp5_get_commands();
|
||||
if (!cmd)
|
||||
return -EINVAL;
|
||||
|
||||
rc = cyttsp5_register_module(&debug_module);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Error, failed registering module\n",
|
||||
__func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_info("%s: Parade TTSP Debug Driver (Built %s) rc=%d\n",
|
||||
__func__, CY_DRIVER_VERSION, rc);
|
||||
return 0;
|
||||
}
|
||||
module_init(cyttsp5_debug_init);
|
||||
|
||||
static void __exit cyttsp5_debug_exit(void)
|
||||
{
|
||||
cyttsp5_unregister_module(&debug_module);
|
||||
}
|
||||
module_exit(cyttsp5_debug_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver");
|
||||
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,750 @@
|
|||
/*
|
||||
* cyttsp5_devtree.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Device Tree Support Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2013-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* cyttsp */
|
||||
#include "cyttsp5_regs.h"
|
||||
#include <linux/platform_data/cyttsp5.h>
|
||||
|
||||
#define ENABLE_VIRTUAL_KEYS
|
||||
|
||||
#define MAX_NAME_LENGTH 64
|
||||
|
||||
enum cyttsp5_device_type {
|
||||
DEVICE_MT,
|
||||
DEVICE_BTN,
|
||||
DEVICE_PROXIMITY,
|
||||
DEVICE_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct cyttsp5_device_pdata_func {
|
||||
void * (*create_and_get_pdata)(struct device_node *);
|
||||
void (*free_pdata)(void *);
|
||||
};
|
||||
|
||||
struct cyttsp5_pdata_ptr {
|
||||
void **pdata;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VIRTUAL_KEYS
|
||||
static struct kobject *board_properties_kobj;
|
||||
|
||||
struct cyttsp5_virtual_keys {
|
||||
struct kobj_attribute kobj_attr;
|
||||
u16 *data;
|
||||
int size;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct cyttsp5_extended_mt_platform_data {
|
||||
struct cyttsp5_mt_platform_data pdata;
|
||||
#ifdef ENABLE_VIRTUAL_KEYS
|
||||
struct cyttsp5_virtual_keys vkeys;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline int get_inp_dev_name(struct device_node *dev_node,
|
||||
const char **inp_dev_name)
|
||||
{
|
||||
return of_property_read_string(dev_node, "cy,inp_dev_name",
|
||||
inp_dev_name);
|
||||
}
|
||||
|
||||
static s16 *create_and_get_u16_array(struct device_node *dev_node,
|
||||
const char *name, int *size)
|
||||
{
|
||||
const __be32 *values;
|
||||
s16 *val_array;
|
||||
int len;
|
||||
int sz;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
values = of_get_property(dev_node, name, &len);
|
||||
if (values == NULL)
|
||||
return NULL;
|
||||
|
||||
sz = len / sizeof(u32);
|
||||
pr_debug("%s: %s size:%d\n", __func__, name, sz);
|
||||
|
||||
val_array = kcalloc(sz, sizeof(s16), GFP_KERNEL);
|
||||
if (!val_array) {
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < sz; i++)
|
||||
val_array[i] = (s16)be32_to_cpup(values++);
|
||||
|
||||
*size = sz;
|
||||
|
||||
return val_array;
|
||||
|
||||
fail:
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static struct touch_framework *create_and_get_touch_framework(
|
||||
struct device_node *dev_node)
|
||||
{
|
||||
struct touch_framework *frmwrk;
|
||||
s16 *abs;
|
||||
int size;
|
||||
int rc;
|
||||
|
||||
abs = create_and_get_u16_array(dev_node, "cy,abs", &size);
|
||||
if (IS_ERR_OR_NULL(abs))
|
||||
return (void *)abs;
|
||||
|
||||
/* Check for valid abs size */
|
||||
if (size % CY_NUM_ABS_SET) {
|
||||
rc = -EINVAL;
|
||||
goto fail_free_abs;
|
||||
}
|
||||
|
||||
frmwrk = kzalloc(sizeof(*frmwrk), GFP_KERNEL);
|
||||
if (!frmwrk) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_free_abs;
|
||||
}
|
||||
|
||||
frmwrk->abs = abs;
|
||||
frmwrk->size = size;
|
||||
|
||||
return frmwrk;
|
||||
|
||||
fail_free_abs:
|
||||
kfree(abs);
|
||||
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void free_touch_framework(struct touch_framework *frmwrk)
|
||||
{
|
||||
kfree(frmwrk->abs);
|
||||
kfree(frmwrk);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VIRTUAL_KEYS
|
||||
#define VIRTUAL_KEY_ELEMENT_SIZE 5
|
||||
static ssize_t virtual_keys_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct cyttsp5_virtual_keys *vkeys = container_of(attr,
|
||||
struct cyttsp5_virtual_keys, kobj_attr);
|
||||
u16 *data = vkeys->data;
|
||||
int size = vkeys->size;
|
||||
int index;
|
||||
int i;
|
||||
|
||||
index = 0;
|
||||
for (i = 0; i < size; i += VIRTUAL_KEY_ELEMENT_SIZE)
|
||||
index += scnprintf(buf + index, CY_MAX_PRBUF_SIZE - index,
|
||||
"0x01:%d:%d:%d:%d:%d\n",
|
||||
data[i], data[i+1], data[i+2], data[i+3], data[i+4]);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int setup_virtual_keys(struct device_node *dev_node,
|
||||
const char *inp_dev_name, struct cyttsp5_virtual_keys *vkeys)
|
||||
{
|
||||
char *name;
|
||||
u16 *data;
|
||||
int size;
|
||||
int rc;
|
||||
|
||||
data = create_and_get_u16_array(dev_node, "cy,virtual_keys", &size);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
else if (IS_ERR(data)) {
|
||||
rc = PTR_ERR(data);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check for valid virtual keys size */
|
||||
if (size % VIRTUAL_KEY_ELEMENT_SIZE) {
|
||||
rc = -EINVAL;
|
||||
goto fail_free_data;
|
||||
}
|
||||
|
||||
name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
|
||||
if (!name) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_free_data;
|
||||
}
|
||||
|
||||
snprintf(name, MAX_NAME_LENGTH, "virtualkeys.%s", inp_dev_name);
|
||||
|
||||
vkeys->data = data;
|
||||
vkeys->size = size;
|
||||
|
||||
/* TODO: Instantiate in board file and export it */
|
||||
if (board_properties_kobj == NULL)
|
||||
board_properties_kobj =
|
||||
kobject_create_and_add("board_properties", NULL);
|
||||
if (board_properties_kobj == NULL) {
|
||||
pr_err("%s: Cannot get board_properties kobject!\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto fail_free_name;
|
||||
}
|
||||
|
||||
/* Initialize dynamic SysFs attribute */
|
||||
sysfs_attr_init(&vkeys->kobj_attr.attr);
|
||||
vkeys->kobj_attr.attr.name = name;
|
||||
vkeys->kobj_attr.attr.mode = S_IRUGO;
|
||||
vkeys->kobj_attr.show = virtual_keys_show;
|
||||
|
||||
rc = sysfs_create_file(board_properties_kobj, &vkeys->kobj_attr.attr);
|
||||
if (rc)
|
||||
goto fail_del_kobj;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_del_kobj:
|
||||
kobject_del(board_properties_kobj);
|
||||
fail_free_name:
|
||||
kfree(name);
|
||||
vkeys->kobj_attr.attr.name = NULL;
|
||||
fail_free_data:
|
||||
kfree(data);
|
||||
vkeys->data = NULL;
|
||||
fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void free_virtual_keys(struct cyttsp5_virtual_keys *vkeys)
|
||||
{
|
||||
if (board_properties_kobj)
|
||||
sysfs_remove_file(board_properties_kobj,
|
||||
&vkeys->kobj_attr.attr);
|
||||
|
||||
|
||||
kobject_del(board_properties_kobj);
|
||||
board_properties_kobj = NULL;
|
||||
|
||||
kfree(vkeys->data);
|
||||
kfree(vkeys->kobj_attr.attr.name);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *create_and_get_mt_pdata(struct device_node *dev_node)
|
||||
{
|
||||
struct cyttsp5_extended_mt_platform_data *ext_pdata;
|
||||
struct cyttsp5_mt_platform_data *pdata;
|
||||
u32 value;
|
||||
int rc;
|
||||
|
||||
ext_pdata = kzalloc(sizeof(*ext_pdata), GFP_KERNEL);
|
||||
if (!ext_pdata) {
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pdata = &ext_pdata->pdata;
|
||||
|
||||
rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
|
||||
if (rc)
|
||||
goto fail_free_pdata;
|
||||
|
||||
/* Optional fields */
|
||||
rc = of_property_read_u32(dev_node, "cy,flags", &value);
|
||||
if (!rc)
|
||||
pdata->flags = value;
|
||||
|
||||
rc = of_property_read_u32(dev_node, "cy,vkeys_x", &value);
|
||||
if (!rc)
|
||||
pdata->vkeys_x = value;
|
||||
|
||||
rc = of_property_read_u32(dev_node, "cy,vkeys_y", &value);
|
||||
if (!rc)
|
||||
pdata->vkeys_y = value;
|
||||
|
||||
/* Required fields */
|
||||
pdata->frmwrk = create_and_get_touch_framework(dev_node);
|
||||
if (pdata->frmwrk == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto fail_free_pdata;
|
||||
} else if (IS_ERR(pdata->frmwrk)) {
|
||||
rc = PTR_ERR(pdata->frmwrk);
|
||||
goto fail_free_pdata;
|
||||
}
|
||||
#ifdef ENABLE_VIRTUAL_KEYS
|
||||
rc = setup_virtual_keys(dev_node, pdata->inp_dev_name,
|
||||
&ext_pdata->vkeys);
|
||||
if (rc) {
|
||||
pr_err("%s: Cannot setup virtual keys!\n", __func__);
|
||||
goto fail_free_pdata;
|
||||
}
|
||||
#endif
|
||||
return pdata;
|
||||
|
||||
fail_free_pdata:
|
||||
kfree(ext_pdata);
|
||||
fail:
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void free_mt_pdata(void *pdata)
|
||||
{
|
||||
struct cyttsp5_mt_platform_data *mt_pdata =
|
||||
(struct cyttsp5_mt_platform_data *)pdata;
|
||||
struct cyttsp5_extended_mt_platform_data *ext_mt_pdata =
|
||||
container_of(mt_pdata,
|
||||
struct cyttsp5_extended_mt_platform_data, pdata);
|
||||
|
||||
free_touch_framework(mt_pdata->frmwrk);
|
||||
#ifdef ENABLE_VIRTUAL_KEYS
|
||||
free_virtual_keys(&ext_mt_pdata->vkeys);
|
||||
#endif
|
||||
kfree(ext_mt_pdata);
|
||||
}
|
||||
|
||||
static void *create_and_get_btn_pdata(struct device_node *dev_node)
|
||||
{
|
||||
struct cyttsp5_btn_platform_data *pdata;
|
||||
int rc;
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
|
||||
if (rc)
|
||||
goto fail_free_pdata;
|
||||
|
||||
return pdata;
|
||||
|
||||
fail_free_pdata:
|
||||
kfree(pdata);
|
||||
fail:
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void free_btn_pdata(void *pdata)
|
||||
{
|
||||
struct cyttsp5_btn_platform_data *btn_pdata =
|
||||
(struct cyttsp5_btn_platform_data *)pdata;
|
||||
|
||||
kfree(btn_pdata);
|
||||
}
|
||||
|
||||
static void *create_and_get_proximity_pdata(struct device_node *dev_node)
|
||||
{
|
||||
struct cyttsp5_proximity_platform_data *pdata;
|
||||
int rc;
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
|
||||
if (rc)
|
||||
goto fail_free_pdata;
|
||||
|
||||
pdata->frmwrk = create_and_get_touch_framework(dev_node);
|
||||
if (pdata->frmwrk == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto fail_free_pdata;
|
||||
} else if (IS_ERR(pdata->frmwrk)) {
|
||||
rc = PTR_ERR(pdata->frmwrk);
|
||||
goto fail_free_pdata;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
|
||||
fail_free_pdata:
|
||||
kfree(pdata);
|
||||
fail:
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void free_proximity_pdata(void *pdata)
|
||||
{
|
||||
struct cyttsp5_proximity_platform_data *proximity_pdata =
|
||||
(struct cyttsp5_proximity_platform_data *)pdata;
|
||||
|
||||
free_touch_framework(proximity_pdata->frmwrk);
|
||||
|
||||
kfree(proximity_pdata);
|
||||
}
|
||||
|
||||
static struct cyttsp5_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = {
|
||||
[DEVICE_MT] = {
|
||||
.create_and_get_pdata = create_and_get_mt_pdata,
|
||||
.free_pdata = free_mt_pdata,
|
||||
},
|
||||
[DEVICE_BTN] = {
|
||||
.create_and_get_pdata = create_and_get_btn_pdata,
|
||||
.free_pdata = free_btn_pdata,
|
||||
},
|
||||
[DEVICE_PROXIMITY] = {
|
||||
.create_and_get_pdata = create_and_get_proximity_pdata,
|
||||
.free_pdata = free_proximity_pdata,
|
||||
},
|
||||
};
|
||||
|
||||
static struct cyttsp5_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX];
|
||||
|
||||
static const char *device_names[DEVICE_TYPE_MAX] = {
|
||||
[DEVICE_MT] = "cy,mt",
|
||||
[DEVICE_BTN] = "cy,btn",
|
||||
[DEVICE_PROXIMITY] = "cy,proximity",
|
||||
};
|
||||
|
||||
static void set_pdata_ptr(struct cyttsp5_platform_data *pdata)
|
||||
{
|
||||
pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata;
|
||||
pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata;
|
||||
pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata;
|
||||
}
|
||||
|
||||
static int get_device_type(struct device_node *dev_node,
|
||||
enum cyttsp5_device_type *type)
|
||||
{
|
||||
const char *name;
|
||||
enum cyttsp5_device_type t;
|
||||
int rc;
|
||||
|
||||
rc = of_property_read_string(dev_node, "name", &name);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (t = 0; t < DEVICE_TYPE_MAX; t++)
|
||||
if (!strncmp(name, device_names[t], MAX_NAME_LENGTH)) {
|
||||
*type = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void *create_and_get_device_pdata(struct device_node *dev_node,
|
||||
enum cyttsp5_device_type type)
|
||||
{
|
||||
return device_pdata_funcs[type].create_and_get_pdata(dev_node);
|
||||
}
|
||||
|
||||
static inline void free_device_pdata(enum cyttsp5_device_type type)
|
||||
{
|
||||
device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata);
|
||||
}
|
||||
|
||||
static struct touch_settings *create_and_get_touch_setting(
|
||||
struct device_node *core_node, const char *name)
|
||||
{
|
||||
struct touch_settings *setting;
|
||||
char *tag_name;
|
||||
u32 tag_value;
|
||||
u16 *data;
|
||||
int size;
|
||||
int rc;
|
||||
|
||||
data = create_and_get_u16_array(core_node, name, &size);
|
||||
if (IS_ERR_OR_NULL(data))
|
||||
return (void *)data;
|
||||
|
||||
pr_debug("%s: Touch setting:'%s' size:%d\n", __func__, name, size);
|
||||
|
||||
setting = kzalloc(sizeof(*setting), GFP_KERNEL);
|
||||
if (!setting) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_free_data;
|
||||
}
|
||||
|
||||
setting->data = (u8 *)data;
|
||||
setting->size = size;
|
||||
|
||||
tag_name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
|
||||
if (!tag_name) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_free_setting;
|
||||
}
|
||||
|
||||
snprintf(tag_name, MAX_NAME_LENGTH, "%s-tag", name);
|
||||
|
||||
rc = of_property_read_u32(core_node, tag_name, &tag_value);
|
||||
if (!rc)
|
||||
setting->tag = tag_value;
|
||||
|
||||
kfree(tag_name);
|
||||
|
||||
return setting;
|
||||
|
||||
fail_free_setting:
|
||||
kfree(setting);
|
||||
fail_free_data:
|
||||
kfree(data);
|
||||
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void free_touch_setting(struct touch_settings *setting)
|
||||
{
|
||||
if (setting) {
|
||||
kfree(setting->data);
|
||||
kfree(setting);
|
||||
}
|
||||
}
|
||||
|
||||
static char *touch_setting_names[CY_IC_GRPNUM_NUM] = {
|
||||
NULL, /* CY_IC_GRPNUM_RESERVED */
|
||||
"cy,cmd_regs", /* CY_IC_GRPNUM_CMD_REGS */
|
||||
"cy,tch_rep", /* CY_IC_GRPNUM_TCH_REP */
|
||||
"cy,data_rec", /* CY_IC_GRPNUM_DATA_REC */
|
||||
"cy,test_rec", /* CY_IC_GRPNUM_TEST_REC */
|
||||
"cy,pcfg_rec", /* CY_IC_GRPNUM_PCFG_REC */
|
||||
"cy,tch_parm_val", /* CY_IC_GRPNUM_TCH_PARM_VAL */
|
||||
"cy,tch_parm_size", /* CY_IC_GRPNUM_TCH_PARM_SIZE */
|
||||
NULL, /* CY_IC_GRPNUM_RESERVED1 */
|
||||
NULL, /* CY_IC_GRPNUM_RESERVED2 */
|
||||
"cy,opcfg_rec", /* CY_IC_GRPNUM_OPCFG_REC */
|
||||
"cy,ddata_rec", /* CY_IC_GRPNUM_DDATA_REC */
|
||||
"cy,mdata_rec", /* CY_IC_GRPNUM_MDATA_REC */
|
||||
"cy,test_regs", /* CY_IC_GRPNUM_TEST_REGS */
|
||||
"cy,btn_keys", /* CY_IC_GRPNUM_BTN_KEYS */
|
||||
NULL, /* CY_IC_GRPNUM_TTHE_REGS */
|
||||
};
|
||||
|
||||
static struct cyttsp5_core_platform_data *create_and_get_core_pdata(
|
||||
struct device_node *core_node)
|
||||
{
|
||||
struct cyttsp5_core_platform_data *pdata;
|
||||
u32 value;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Required fields */
|
||||
pdata->irq_gpio = of_get_named_gpio(core_node, "cy,irq_gpio", 0);
|
||||
if (!gpio_is_valid(pdata->irq_gpio)) {
|
||||
rc = -ENODEV;
|
||||
pr_err("Invalid irq_gpio");
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(core_node, "cy,hid_desc_register", &value);
|
||||
if (rc)
|
||||
goto fail_free;
|
||||
pdata->hid_desc_register = value;
|
||||
|
||||
/* Optional fields */
|
||||
/* rst_gpio is optional since a platform may use
|
||||
* power cycling instead of using the XRES pin
|
||||
*/
|
||||
pdata->rst_gpio = of_get_named_gpio(core_node, "cy,rst_gpio", 0);
|
||||
|
||||
rc = of_property_read_u32(core_node, "cy,level_irq_udelay", &value);
|
||||
if (!rc)
|
||||
pdata->level_irq_udelay = value;
|
||||
|
||||
rc = of_property_read_u32(core_node, "cy,vendor_id", &value);
|
||||
if (!rc)
|
||||
pdata->vendor_id = value;
|
||||
|
||||
rc = of_property_read_u32(core_node, "cy,product_id", &value);
|
||||
if (!rc)
|
||||
pdata->product_id = value;
|
||||
|
||||
rc = of_property_read_u32(core_node, "cy,flags", &value);
|
||||
if (!rc)
|
||||
pdata->flags = value;
|
||||
|
||||
rc = of_property_read_u32(core_node, "cy,easy_wakeup_gesture", &value);
|
||||
if (!rc)
|
||||
pdata->easy_wakeup_gesture = (u8)value;
|
||||
|
||||
pdata->fb_blanking_disabled = of_property_read_bool(core_node, "cy,fb_blanking_disabled");
|
||||
|
||||
for (i = 0; (unsigned int)i < ARRAY_SIZE(touch_setting_names); i++) {
|
||||
if (touch_setting_names[i] == NULL)
|
||||
continue;
|
||||
|
||||
pdata->sett[i] = create_and_get_touch_setting(core_node,
|
||||
touch_setting_names[i]);
|
||||
if (IS_ERR(pdata->sett[i])) {
|
||||
rc = PTR_ERR(pdata->sett[i]);
|
||||
goto fail_free_sett;
|
||||
} else if (pdata->sett[i] == NULL)
|
||||
pr_debug("%s: No data for setting '%s'\n", __func__,
|
||||
touch_setting_names[i]);
|
||||
}
|
||||
|
||||
pr_debug("%s: irq_gpio:%d rst_gpio:%d\n"
|
||||
"hid_desc_register:%d level_irq_udelay:%d vendor_id:%d product_id:%d\n"
|
||||
"flags:%d easy_wakeup_gesture:%d\n", __func__,
|
||||
pdata->irq_gpio, pdata->rst_gpio,
|
||||
pdata->hid_desc_register,
|
||||
pdata->level_irq_udelay, pdata->vendor_id, pdata->product_id,
|
||||
pdata->flags, pdata->easy_wakeup_gesture);
|
||||
|
||||
pdata->xres = cyttsp5_xres;
|
||||
pdata->init = cyttsp5_init;
|
||||
pdata->power = cyttsp5_power;
|
||||
pdata->detect = cyttsp5_detect;
|
||||
pdata->irq_stat = cyttsp5_irq_stat;
|
||||
|
||||
return pdata;
|
||||
|
||||
fail_free_sett:
|
||||
for (i--; i >= 0; i--)
|
||||
free_touch_setting(pdata->sett[i]);
|
||||
fail_free:
|
||||
kfree(pdata);
|
||||
fail:
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void free_core_pdata(void *pdata)
|
||||
{
|
||||
struct cyttsp5_core_platform_data *core_pdata = pdata;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(touch_setting_names); i++)
|
||||
free_touch_setting(core_pdata->sett[i]);
|
||||
kfree(core_pdata);
|
||||
}
|
||||
|
||||
int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev)
|
||||
{
|
||||
struct cyttsp5_platform_data *pdata;
|
||||
struct device_node *core_node, *dev_node, *dev_node_fail;
|
||||
enum cyttsp5_device_type type;
|
||||
int count = 0;
|
||||
int rc = 0;
|
||||
|
||||
if (!adap_dev->of_node)
|
||||
return 0;
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
adap_dev->platform_data = pdata;
|
||||
set_pdata_ptr(pdata);
|
||||
|
||||
/* There should be only one core node */
|
||||
for_each_child_of_node(adap_dev->of_node, core_node) {
|
||||
const char *name;
|
||||
|
||||
rc = of_property_read_string(core_node, "name", &name);
|
||||
if (!rc)
|
||||
pr_debug("%s: name:%s\n", __func__, name);
|
||||
|
||||
pdata->core_pdata = create_and_get_core_pdata(core_node);
|
||||
if (IS_ERR(pdata->core_pdata)) {
|
||||
rc = PTR_ERR(pdata->core_pdata);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Increment reference count */
|
||||
of_node_get(core_node);
|
||||
|
||||
for_each_child_of_node(core_node, dev_node) {
|
||||
count++;
|
||||
rc = get_device_type(dev_node, &type);
|
||||
if (rc)
|
||||
break;
|
||||
*pdata_ptr[type].pdata
|
||||
= create_and_get_device_pdata(dev_node, type);
|
||||
if (IS_ERR(*pdata_ptr[type].pdata))
|
||||
rc = PTR_ERR(*pdata_ptr[type].pdata);
|
||||
if (rc)
|
||||
break;
|
||||
/* Increment reference count */
|
||||
of_node_get(dev_node);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
free_core_pdata(pdata->core_pdata);
|
||||
of_node_put(core_node);
|
||||
for_each_child_of_node(core_node, dev_node_fail) {
|
||||
if (dev_node == dev_node_fail)
|
||||
break;
|
||||
rc = get_device_type(dev_node, &type);
|
||||
if (rc)
|
||||
break;
|
||||
free_device_pdata(type);
|
||||
of_node_put(dev_node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
pdata->loader_pdata = &_cyttsp5_loader_platform_data;
|
||||
}
|
||||
|
||||
pr_debug("%s: %d child node(s) found\n", __func__, count);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cyttsp5_devtree_create_and_get_pdata);
|
||||
|
||||
int cyttsp5_devtree_clean_pdata(struct device *adap_dev)
|
||||
{
|
||||
struct cyttsp5_platform_data *pdata;
|
||||
struct device_node *core_node, *dev_node;
|
||||
enum cyttsp5_device_type type;
|
||||
int rc = 0;
|
||||
|
||||
if (!adap_dev->of_node)
|
||||
return 0;
|
||||
|
||||
pdata = dev_get_platdata(adap_dev);
|
||||
set_pdata_ptr(pdata);
|
||||
for_each_child_of_node(adap_dev->of_node, core_node) {
|
||||
free_core_pdata(pdata->core_pdata);
|
||||
of_node_put(core_node);
|
||||
for_each_child_of_node(core_node, dev_node) {
|
||||
rc = get_device_type(dev_node, &type);
|
||||
if (rc)
|
||||
break;
|
||||
free_device_pdata(type);
|
||||
of_node_put(dev_node);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cyttsp5_devtree_clean_pdata);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver");
|
||||
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* cyttsp5_i2c.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 I2C Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define CY_I2C_DATA_SIZE (2 * 256)
|
||||
|
||||
static int cyttsp5_i2c_read_default(struct device *dev, void *buf, int size)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int rc;
|
||||
|
||||
if (!buf || !size || size > CY_I2C_DATA_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
rc = i2c_master_recv(client, buf, size);
|
||||
|
||||
return (rc < 0) ? rc : rc != size ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msgs[2];
|
||||
u8 msg_count = 1;
|
||||
int rc;
|
||||
u32 size;
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
|
||||
msgs[0].len = 2;
|
||||
msgs[0].buf = buf;
|
||||
rc = i2c_transfer(client->adapter, msgs, msg_count);
|
||||
if (rc < 0 || rc != msg_count)
|
||||
return (rc < 0) ? rc : -EIO;
|
||||
|
||||
size = get_unaligned_le16(&buf[0]);
|
||||
if (!size || size == 2 || size >= CY_PIP_1P7_EMPTY_BUF)
|
||||
/* Before PIP 1.7, empty buffer is 0x0002;
|
||||
From PIP 1.7, empty buffer is 0xFFXX */
|
||||
return 0;
|
||||
|
||||
if (size > max)
|
||||
return -EINVAL;
|
||||
|
||||
rc = i2c_master_recv(client, buf, size);
|
||||
|
||||
return (rc < 0) ? rc : rc != (int)size ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_i2c_write_read_specific(struct device *dev, u8 write_len,
|
||||
u8 *write_buf, u8 *read_buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msgs[2];
|
||||
u8 msg_count = 1;
|
||||
int rc;
|
||||
|
||||
if (!write_buf || !write_len)
|
||||
return -EINVAL;
|
||||
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = client->flags & I2C_M_TEN;
|
||||
msgs[0].len = write_len;
|
||||
msgs[0].buf = write_buf;
|
||||
rc = i2c_transfer(client->adapter, msgs, msg_count);
|
||||
|
||||
if (rc < 0 || rc != msg_count)
|
||||
return (rc < 0) ? rc : -EIO;
|
||||
|
||||
rc = 0;
|
||||
|
||||
if (read_buf)
|
||||
rc = cyttsp5_i2c_read_default_nosize(dev, read_buf,
|
||||
CY_I2C_DATA_SIZE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct cyttsp5_bus_ops cyttsp5_i2c_bus_ops = {
|
||||
.bustype = BUS_I2C,
|
||||
.read_default = cyttsp5_i2c_read_default,
|
||||
.read_default_nosize = cyttsp5_i2c_read_default_nosize,
|
||||
.write_read_specific = cyttsp5_i2c_write_read_specific,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
static const struct of_device_id cyttsp5_i2c_of_match[] = {
|
||||
{ .compatible = "cy,cyttsp5_i2c_adapter", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cyttsp5_i2c_of_match);
|
||||
#endif
|
||||
|
||||
static int cyttsp5_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *i2c_id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
const struct of_device_id *match;
|
||||
#endif
|
||||
int rc;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(dev, "I2C functionality not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
match = of_match_device(of_match_ptr(cyttsp5_i2c_of_match), dev);
|
||||
if (match) {
|
||||
rc = cyttsp5_devtree_create_and_get_pdata(dev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = cyttsp5_probe(&cyttsp5_i2c_bus_ops, &client->dev, client->irq,
|
||||
CY_I2C_DATA_SIZE);
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
if (rc && match)
|
||||
cyttsp5_devtree_clean_pdata(dev);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
struct device *dev = &client->dev;
|
||||
const struct of_device_id *match;
|
||||
#endif
|
||||
struct cyttsp5_core_data *cd = i2c_get_clientdata(client);
|
||||
|
||||
cyttsp5_release(cd);
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
match = of_match_device(of_match_ptr(cyttsp5_i2c_of_match), dev);
|
||||
if (match)
|
||||
cyttsp5_devtree_clean_pdata(dev);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cyttsp5_i2c_id[] = {
|
||||
{ CYTTSP5_I2C_NAME, 0, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id);
|
||||
|
||||
static struct i2c_driver cyttsp5_i2c_driver = {
|
||||
.driver = {
|
||||
.name = CYTTSP5_I2C_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyttsp5_pm_ops,
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
.of_match_table = cyttsp5_i2c_of_match,
|
||||
#endif
|
||||
},
|
||||
.probe = cyttsp5_i2c_probe,
|
||||
.remove = cyttsp5_i2c_remove,
|
||||
.id_table = cyttsp5_i2c_id,
|
||||
};
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
|
||||
module_i2c_driver(cyttsp5_i2c_driver);
|
||||
#else
|
||||
static int __init cyttsp5_i2c_init(void)
|
||||
{
|
||||
int rc = i2c_add_driver(&cyttsp5_i2c_driver);
|
||||
|
||||
pr_info("%s: Parade TTSP I2C Driver (Built %s) rc=%d\n",
|
||||
__func__, CY_DRIVER_VERSION, rc);
|
||||
return rc;
|
||||
}
|
||||
module_init(cyttsp5_i2c_init);
|
||||
|
||||
static void __exit cyttsp5_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&cyttsp5_i2c_driver);
|
||||
}
|
||||
module_exit(cyttsp5_i2c_exit);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product I2C driver");
|
||||
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,758 @@
|
|||
/*
|
||||
* cyttsp5_mt_common.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Multi-Touch Reports Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
#define CYTTSP5_MT_NAME "cyttsp5_mt"
|
||||
|
||||
#define MT_PARAM_SIGNAL(md, sig_ost) PARAM_SIGNAL(md->pdata->frmwrk, sig_ost)
|
||||
#define MT_PARAM_MIN(md, sig_ost) PARAM_MIN(md->pdata->frmwrk, sig_ost)
|
||||
#define MT_PARAM_MAX(md, sig_ost) PARAM_MAX(md->pdata->frmwrk, sig_ost)
|
||||
#define MT_PARAM_FUZZ(md, sig_ost) PARAM_FUZZ(md->pdata->frmwrk, sig_ost)
|
||||
#define MT_PARAM_FLAT(md, sig_ost) PARAM_FLAT(md->pdata->frmwrk, sig_ost)
|
||||
|
||||
void cyttsp5_pr_buf_debug(struct device *dev, u8 *dptr, int size,
|
||||
const char *data_name)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
u8 *pr_buf = cd->pr_buf;
|
||||
int i, k;
|
||||
const char fmt[] = "%02X ";
|
||||
int max;
|
||||
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
|
||||
|
||||
pr_buf[0] = 0;
|
||||
for (i = k = 0; i < size && k < max; i++, k += 3)
|
||||
scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]);
|
||||
|
||||
if (size)
|
||||
dev_err(dev, "%s: %s[0..%d]=%s%s\n", __func__, data_name,
|
||||
size - 1, pr_buf, size <= max ? "" : CY_PR_TRUNCATED);
|
||||
else
|
||||
dev_err(dev, "%s: %s[]\n", __func__, data_name);
|
||||
}
|
||||
|
||||
static void cyttsp5_mt_lift_all(struct cyttsp5_mt_data *md)
|
||||
{
|
||||
int max = md->si->tch_abs[CY_TCH_T].max;
|
||||
|
||||
if (md->num_prv_rec != 0) {
|
||||
if (md->mt_function.report_slot_liftoff)
|
||||
md->mt_function.report_slot_liftoff(md, max);
|
||||
input_sync(md->input);
|
||||
md->num_prv_rec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void cyttsp5_get_touch_axis(struct cyttsp5_mt_data *md,
|
||||
int *axis, int size, int max, u8 *xy_data, int bofs)
|
||||
{
|
||||
int nbyte;
|
||||
int next;
|
||||
|
||||
for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
|
||||
dev_vdbg(md->dev,
|
||||
"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
|
||||
__func__, *axis, *axis, size, max, xy_data, next,
|
||||
xy_data[next], xy_data[next], bofs);
|
||||
*axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
|
||||
next++;
|
||||
}
|
||||
|
||||
*axis &= max - 1;
|
||||
|
||||
dev_vdbg(md->dev,
|
||||
"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
|
||||
__func__, *axis, *axis, size, max, xy_data, next,
|
||||
xy_data[next], xy_data[next]);
|
||||
}
|
||||
|
||||
static void cyttsp5_get_touch_hdr(struct cyttsp5_mt_data *md,
|
||||
struct cyttsp5_touch *touch, u8 *xy_mode)
|
||||
{
|
||||
struct device *dev = md->dev;
|
||||
struct cyttsp5_sysinfo *si = md->si;
|
||||
enum cyttsp5_tch_hdr hdr;
|
||||
|
||||
for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) {
|
||||
if (!si->tch_hdr[hdr].report)
|
||||
continue;
|
||||
cyttsp5_get_touch_axis(md, &touch->hdr[hdr],
|
||||
si->tch_hdr[hdr].size,
|
||||
si->tch_hdr[hdr].max,
|
||||
xy_mode + si->tch_hdr[hdr].ofs,
|
||||
si->tch_hdr[hdr].bofs);
|
||||
dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
|
||||
cyttsp5_tch_hdr_string[hdr],
|
||||
touch->hdr[hdr], touch->hdr[hdr]);
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"%s: time=%X tch_num=%d lo=%d noise=%d counter=%d\n",
|
||||
__func__,
|
||||
touch->hdr[CY_TCH_TIME],
|
||||
touch->hdr[CY_TCH_NUM],
|
||||
touch->hdr[CY_TCH_LO],
|
||||
touch->hdr[CY_TCH_NOISE],
|
||||
touch->hdr[CY_TCH_COUNTER]);
|
||||
}
|
||||
|
||||
static void cyttsp5_get_touch_record(struct cyttsp5_mt_data *md,
|
||||
struct cyttsp5_touch *touch, u8 *xy_data)
|
||||
{
|
||||
struct device *dev = md->dev;
|
||||
struct cyttsp5_sysinfo *si = md->si;
|
||||
enum cyttsp5_tch_abs abs;
|
||||
|
||||
for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
|
||||
if (!si->tch_abs[abs].report)
|
||||
continue;
|
||||
cyttsp5_get_touch_axis(md, &touch->abs[abs],
|
||||
si->tch_abs[abs].size,
|
||||
si->tch_abs[abs].max,
|
||||
xy_data + si->tch_abs[abs].ofs,
|
||||
si->tch_abs[abs].bofs);
|
||||
dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
|
||||
cyttsp5_tch_abs_string[abs],
|
||||
touch->abs[abs], touch->abs[abs]);
|
||||
}
|
||||
}
|
||||
|
||||
static void cyttsp5_mt_process_touch(struct cyttsp5_mt_data *md,
|
||||
struct cyttsp5_touch *touch)
|
||||
{
|
||||
struct device *dev = md->dev;
|
||||
struct cyttsp5_sysinfo *si = md->si;
|
||||
int tmp;
|
||||
bool flipped;
|
||||
|
||||
|
||||
/* Orientation is signed */
|
||||
touch->abs[CY_TCH_OR] = (int8_t)touch->abs[CY_TCH_OR];
|
||||
|
||||
if (md->pdata->flags & CY_MT_FLAG_FLIP) {
|
||||
tmp = touch->abs[CY_TCH_X];
|
||||
touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y];
|
||||
touch->abs[CY_TCH_Y] = tmp;
|
||||
if (touch->abs[CY_TCH_OR] > 0)
|
||||
touch->abs[CY_TCH_OR] =
|
||||
md->or_max - touch->abs[CY_TCH_OR];
|
||||
else
|
||||
touch->abs[CY_TCH_OR] =
|
||||
md->or_min - touch->abs[CY_TCH_OR];
|
||||
flipped = true;
|
||||
} else
|
||||
flipped = false;
|
||||
|
||||
if (md->pdata->flags & CY_MT_FLAG_INV_X) {
|
||||
if (flipped)
|
||||
touch->abs[CY_TCH_X] = si->sensing_conf_data.res_y -
|
||||
touch->abs[CY_TCH_X];
|
||||
else
|
||||
touch->abs[CY_TCH_X] = si->sensing_conf_data.res_x -
|
||||
touch->abs[CY_TCH_X];
|
||||
touch->abs[CY_TCH_OR] *= -1;
|
||||
}
|
||||
if (md->pdata->flags & CY_MT_FLAG_INV_Y) {
|
||||
if (flipped)
|
||||
touch->abs[CY_TCH_Y] = si->sensing_conf_data.res_x -
|
||||
touch->abs[CY_TCH_Y];
|
||||
else
|
||||
touch->abs[CY_TCH_Y] = si->sensing_conf_data.res_y -
|
||||
touch->abs[CY_TCH_Y];
|
||||
touch->abs[CY_TCH_OR] *= -1;
|
||||
}
|
||||
|
||||
/* Convert MAJOR/MINOR from mm to resolution */
|
||||
tmp = touch->abs[CY_TCH_MAJ] * 100 * si->sensing_conf_data.res_x;
|
||||
touch->abs[CY_TCH_MAJ] = tmp / si->sensing_conf_data.len_x;
|
||||
tmp = touch->abs[CY_TCH_MIN] * 100 * si->sensing_conf_data.res_x;
|
||||
touch->abs[CY_TCH_MIN] = tmp / si->sensing_conf_data.len_x;
|
||||
|
||||
dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
|
||||
__func__, flipped ? "true" : "false",
|
||||
md->pdata->flags & CY_MT_FLAG_INV_X ? "true" : "false",
|
||||
md->pdata->flags & CY_MT_FLAG_INV_Y ? "true" : "false",
|
||||
touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
|
||||
touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
|
||||
}
|
||||
|
||||
static void cyttsp5_report_event(struct cyttsp5_mt_data *md, int event,
|
||||
int value)
|
||||
{
|
||||
int sig = MT_PARAM_SIGNAL(md, event);
|
||||
|
||||
if (sig != CY_IGNORE_VALUE)
|
||||
input_report_abs(md->input, sig, value);
|
||||
}
|
||||
|
||||
static void cyttsp5_get_mt_touches(struct cyttsp5_mt_data *md,
|
||||
struct cyttsp5_touch *tch, int num_cur_tch)
|
||||
{
|
||||
struct device *dev = md->dev;
|
||||
struct cyttsp5_sysinfo *si = md->si;
|
||||
int sig;
|
||||
int i, j, t = 0;
|
||||
DECLARE_BITMAP(ids, si->tch_abs[CY_TCH_T].max);
|
||||
int mt_sync_count = 0;
|
||||
u8 *tch_addr;
|
||||
|
||||
bitmap_zero(ids, si->tch_abs[CY_TCH_T].max);
|
||||
memset(tch->abs, 0, sizeof(tch->abs));
|
||||
|
||||
for (i = 0; i < num_cur_tch; i++) {
|
||||
tch_addr = si->xy_data + (i * si->desc.tch_record_size);
|
||||
cyttsp5_get_touch_record(md, tch, tch_addr);
|
||||
|
||||
/* Discard proximity event */
|
||||
if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) {
|
||||
dev_vdbg(dev, "%s: Discarding proximity event\n",
|
||||
__func__);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Validate track_id */
|
||||
t = tch->abs[CY_TCH_T];
|
||||
if (t < md->t_min || t > md->t_max) {
|
||||
dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n",
|
||||
__func__, i, t, md->t_max);
|
||||
if (md->mt_function.input_sync)
|
||||
md->mt_function.input_sync(md->input);
|
||||
mt_sync_count++;
|
||||
cyttsp5_pr_buf_debug(dev, si->xy_mode, si->desc.tch_header_size,
|
||||
"Err xy_mode");
|
||||
cyttsp5_pr_buf_debug(dev, si->xy_data,
|
||||
num_cur_tch* si->desc.tch_record_size, "Err xy_data");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Lift-off */
|
||||
if (tch->abs[CY_TCH_E] == CY_EV_LIFTOFF) {
|
||||
dev_dbg(dev, "%s: t=%d e=%d lift-off\n",
|
||||
__func__, t, tch->abs[CY_TCH_E]);
|
||||
goto cyttsp5_get_mt_touches_pr_tch;
|
||||
}
|
||||
|
||||
/* Process touch */
|
||||
cyttsp5_mt_process_touch(md, tch);
|
||||
|
||||
/* use 0 based track id's */
|
||||
t -= md->t_min;
|
||||
|
||||
sig = MT_PARAM_SIGNAL(md, CY_ABS_ID_OST);
|
||||
if (sig != CY_IGNORE_VALUE) {
|
||||
if (md->mt_function.input_report)
|
||||
md->mt_function.input_report(md->input, sig,
|
||||
t, tch->abs[CY_TCH_O]);
|
||||
__set_bit(t, ids);
|
||||
}
|
||||
|
||||
/* If touch type is hover, send P as distance, reset P */
|
||||
if (tch->abs[CY_TCH_O] == CY_OBJ_HOVER) {
|
||||
/* CY_ABS_D_OST signal must be in touch framework */
|
||||
cyttsp5_report_event(md, CY_ABS_D_OST,
|
||||
tch->abs[CY_TCH_P]);
|
||||
tch->abs[CY_TCH_P] = 0;
|
||||
} else
|
||||
cyttsp5_report_event(md, CY_ABS_D_OST, 0);
|
||||
|
||||
|
||||
/* all devices: position and pressure fields */
|
||||
for (j = 0; j <= CY_ABS_W_OST; j++) {
|
||||
if (!si->tch_abs[j].report)
|
||||
continue;
|
||||
cyttsp5_report_event(md, CY_ABS_X_OST + j,
|
||||
tch->abs[CY_TCH_X + j]);
|
||||
}
|
||||
|
||||
/* Get the extended touch fields */
|
||||
for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) {
|
||||
if (!si->tch_abs[CY_ABS_MAJ_OST + j].report)
|
||||
continue;
|
||||
cyttsp5_report_event(md, CY_ABS_MAJ_OST + j,
|
||||
tch->abs[CY_TCH_MAJ + j]);
|
||||
}
|
||||
if (md->mt_function.input_sync)
|
||||
md->mt_function.input_sync(md->input);
|
||||
mt_sync_count++;
|
||||
|
||||
cyttsp5_get_mt_touches_pr_tch:
|
||||
dev_dbg(dev,
|
||||
"%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d obj=%d tip=%d\n",
|
||||
__func__, t,
|
||||
tch->abs[CY_TCH_X],
|
||||
tch->abs[CY_TCH_Y],
|
||||
tch->abs[CY_TCH_P],
|
||||
tch->abs[CY_TCH_MAJ],
|
||||
tch->abs[CY_TCH_MIN],
|
||||
tch->abs[CY_TCH_OR],
|
||||
tch->abs[CY_TCH_E],
|
||||
tch->abs[CY_TCH_O],
|
||||
tch->abs[CY_TCH_TIP]);
|
||||
}
|
||||
|
||||
if (md->mt_function.final_sync)
|
||||
md->mt_function.final_sync(md->input,
|
||||
si->tch_abs[CY_TCH_T].max, mt_sync_count, ids);
|
||||
|
||||
md->num_prv_rec = num_cur_tch;
|
||||
}
|
||||
|
||||
/* read xy_data for all current touches */
|
||||
static int cyttsp5_xy_worker(struct cyttsp5_mt_data *md)
|
||||
{
|
||||
struct device *dev = md->dev;
|
||||
struct cyttsp5_sysinfo *si = md->si;
|
||||
int max_tch = si->sensing_conf_data.max_tch;
|
||||
struct cyttsp5_touch tch;
|
||||
u8 num_cur_tch;
|
||||
int rc = 0;
|
||||
|
||||
cyttsp5_get_touch_hdr(md, &tch, si->xy_mode + 3);
|
||||
|
||||
num_cur_tch = tch.hdr[CY_TCH_NUM];
|
||||
if (num_cur_tch > max_tch) {
|
||||
dev_err(dev, "%s: Num touch err detected (n=%d)\n",
|
||||
__func__, num_cur_tch);
|
||||
num_cur_tch = max_tch;
|
||||
}
|
||||
|
||||
if (tch.hdr[CY_TCH_LO]) {
|
||||
dev_dbg(dev, "%s: Large area detected\n", __func__);
|
||||
if (md->pdata->flags & CY_MT_FLAG_NO_TOUCH_ON_LO)
|
||||
num_cur_tch = 0;
|
||||
}
|
||||
|
||||
if (num_cur_tch == 0 && md->num_prv_rec == 0)
|
||||
goto cyttsp5_xy_worker_exit;
|
||||
|
||||
/* extract xy_data for all currently reported touches */
|
||||
dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__,
|
||||
num_cur_tch);
|
||||
if (num_cur_tch)
|
||||
cyttsp5_get_mt_touches(md, &tch, num_cur_tch);
|
||||
else
|
||||
cyttsp5_mt_lift_all(md);
|
||||
|
||||
rc = 0;
|
||||
|
||||
cyttsp5_xy_worker_exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void cyttsp5_mt_send_dummy_event(struct cyttsp5_core_data *cd,
|
||||
struct cyttsp5_mt_data *md)
|
||||
{
|
||||
#ifndef EASYWAKE_TSG6
|
||||
/* TSG5 EasyWake */
|
||||
unsigned long ids = 0;
|
||||
|
||||
/* for easy wakeup */
|
||||
if (md->mt_function.input_report)
|
||||
md->mt_function.input_report(md->input, ABS_MT_TRACKING_ID,
|
||||
0, CY_OBJ_STANDARD_FINGER);
|
||||
if (md->mt_function.input_sync)
|
||||
md->mt_function.input_sync(md->input);
|
||||
if (md->mt_function.final_sync)
|
||||
md->mt_function.final_sync(md->input, 0, 1, &ids);
|
||||
if (md->mt_function.report_slot_liftoff)
|
||||
md->mt_function.report_slot_liftoff(md, 1);
|
||||
if (md->mt_function.final_sync)
|
||||
md->mt_function.final_sync(md->input, 1, 1, &ids);
|
||||
#else
|
||||
/* TSG6 FW1.3 EasyWake */
|
||||
u8 key_value;
|
||||
|
||||
switch (cd->gesture_id) {
|
||||
case GESTURE_DOUBLE_TAP:
|
||||
key_value = KEY_F1;
|
||||
break;
|
||||
case GESTURE_TWO_FINGERS_SLIDE:
|
||||
key_value = KEY_F2;
|
||||
break;
|
||||
case GESTURE_TOUCH_DETECTED:
|
||||
key_value = KEY_F3;
|
||||
break;
|
||||
case GESTURE_PUSH_BUTTON:
|
||||
key_value = KEY_F4;
|
||||
break;
|
||||
case GESTURE_SINGLE_SLIDE_DE_TX:
|
||||
key_value = KEY_F5;
|
||||
break;
|
||||
case GESTURE_SINGLE_SLIDE_IN_TX:
|
||||
key_value = KEY_F6;
|
||||
break;
|
||||
case GESTURE_SINGLE_SLIDE_DE_RX:
|
||||
key_value = KEY_F7;
|
||||
break;
|
||||
case GESTURE_SINGLE_SLIDE_IN_RX:
|
||||
key_value = KEY_F8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_key(md->input, key_value, 1);
|
||||
mdelay(10);
|
||||
input_report_key(md->input, key_value, 0);
|
||||
input_sync(md->input);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int cyttsp5_mt_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
int rc;
|
||||
|
||||
if (md->si->xy_mode[2] != md->si->desc.tch_report_id)
|
||||
return 0;
|
||||
|
||||
/* core handles handshake */
|
||||
mutex_lock(&md->mt_lock);
|
||||
rc = cyttsp5_xy_worker(md);
|
||||
mutex_unlock(&md->mt_lock);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_mt_wake_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
mutex_lock(&md->mt_lock);
|
||||
cyttsp5_mt_send_dummy_event(cd, md);
|
||||
mutex_unlock(&md->mt_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_startup_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
mutex_lock(&md->mt_lock);
|
||||
cyttsp5_mt_lift_all(md);
|
||||
mutex_unlock(&md->mt_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_mt_suspend_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
mutex_lock(&md->mt_lock);
|
||||
cyttsp5_mt_lift_all(md);
|
||||
md->is_suspended = true;
|
||||
mutex_unlock(&md->mt_lock);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_mt_resume_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
pm_runtime_get(dev);
|
||||
|
||||
mutex_lock(&md->mt_lock);
|
||||
md->is_suspended = false;
|
||||
mutex_unlock(&md->mt_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_mt_open(struct input_dev *input)
|
||||
{
|
||||
struct device *dev = input->dev.parent;
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
mutex_lock(&md->mt_lock);
|
||||
md->is_suspended = false;
|
||||
mutex_unlock(&md->mt_lock);
|
||||
|
||||
dev_vdbg(dev, "%s: setup subscriptions\n", __func__);
|
||||
|
||||
/* set up touch call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_attention, CY_MODE_OPERATIONAL);
|
||||
|
||||
/* set up startup call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME,
|
||||
cyttsp5_startup_attention, 0);
|
||||
|
||||
/* set up wakeup call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_WAKE, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_wake_attention, 0);
|
||||
|
||||
/* set up suspend call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_suspend_attention, 0);
|
||||
|
||||
/* set up resume call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_resume_attention, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cyttsp5_mt_close(struct input_dev *input)
|
||||
{
|
||||
struct device *dev = input->dev.parent;
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_attention, CY_MODE_OPERATIONAL);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME,
|
||||
cyttsp5_startup_attention, 0);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_WAKE, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_wake_attention, 0);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_suspend_attention, 0);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_MT_NAME,
|
||||
cyttsp5_mt_resume_attention, 0);
|
||||
|
||||
mutex_lock(&md->mt_lock);
|
||||
if (!md->is_suspended) {
|
||||
pm_runtime_put(dev);
|
||||
md->is_suspended = true;
|
||||
}
|
||||
mutex_unlock(&md->mt_lock);
|
||||
}
|
||||
|
||||
static int cyttsp5_setup_input_device(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
int signal = CY_IGNORE_VALUE;
|
||||
int max_x, max_y, max_p, min, max;
|
||||
int max_x_tmp, max_y_tmp;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
|
||||
__set_bit(EV_ABS, md->input->evbit);
|
||||
__set_bit(EV_REL, md->input->evbit);
|
||||
__set_bit(EV_KEY, md->input->evbit);
|
||||
#ifdef INPUT_PROP_DIRECT
|
||||
__set_bit(INPUT_PROP_DIRECT, md->input->propbit);
|
||||
#endif
|
||||
|
||||
/* If virtualkeys enabled, don't use all screen */
|
||||
if (md->pdata->flags & CY_MT_FLAG_VKEYS) {
|
||||
max_x_tmp = md->pdata->vkeys_x;
|
||||
max_y_tmp = md->pdata->vkeys_y;
|
||||
} else {
|
||||
max_x_tmp = md->si->sensing_conf_data.res_x;
|
||||
max_y_tmp = md->si->sensing_conf_data.res_y;
|
||||
}
|
||||
|
||||
/* get maximum values from the sysinfo data */
|
||||
if (md->pdata->flags & CY_MT_FLAG_FLIP) {
|
||||
max_x = max_y_tmp - 1;
|
||||
max_y = max_x_tmp - 1;
|
||||
} else {
|
||||
max_x = max_x_tmp - 1;
|
||||
max_y = max_y_tmp - 1;
|
||||
}
|
||||
max_p = md->si->sensing_conf_data.max_z;
|
||||
|
||||
/* set event signal capabilities */
|
||||
for (i = 0; i < NUM_SIGNALS(md->pdata->frmwrk); i++) {
|
||||
signal = MT_PARAM_SIGNAL(md, i);
|
||||
if (signal != CY_IGNORE_VALUE) {
|
||||
__set_bit(signal, md->input->absbit);
|
||||
|
||||
min = MT_PARAM_MIN(md, i);
|
||||
max = MT_PARAM_MAX(md, i);
|
||||
if (i == CY_ABS_ID_OST) {
|
||||
/* shift track ids down to start at 0 */
|
||||
max = max - min;
|
||||
min = min - min;
|
||||
} else if (i == CY_ABS_X_OST)
|
||||
max = max_x;
|
||||
else if (i == CY_ABS_Y_OST)
|
||||
max = max_y;
|
||||
else if (i == CY_ABS_P_OST)
|
||||
max = max_p;
|
||||
|
||||
input_set_abs_params(md->input, signal, min, max,
|
||||
MT_PARAM_FUZZ(md, i), MT_PARAM_FLAT(md, i));
|
||||
dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n",
|
||||
__func__, signal, min, max);
|
||||
}
|
||||
}
|
||||
|
||||
md->or_min = MT_PARAM_MIN(md, CY_ABS_OR_OST);
|
||||
md->or_max = MT_PARAM_MAX(md, CY_ABS_OR_OST);
|
||||
|
||||
md->t_min = MT_PARAM_MIN(md, CY_ABS_ID_OST);
|
||||
md->t_max = MT_PARAM_MAX(md, CY_ABS_ID_OST);
|
||||
|
||||
rc = md->mt_function.input_register_device(md->input,
|
||||
md->si->tch_abs[CY_TCH_T].max);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: Error, failed register input device r=%d\n",
|
||||
__func__, rc);
|
||||
else
|
||||
md->input_device_registered = true;
|
||||
|
||||
#ifdef EASYWAKE_TSG6
|
||||
input_set_capability(md->input, EV_KEY, KEY_F1);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F2);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F3);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F4);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F5);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F6);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F7);
|
||||
input_set_capability(md->input, EV_KEY, KEY_F8);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_setup_input_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
int rc;
|
||||
|
||||
md->si = _cyttsp5_request_sysinfo(dev);
|
||||
if (!md->si)
|
||||
return -EINVAL;
|
||||
|
||||
rc = cyttsp5_setup_input_device(dev);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME,
|
||||
cyttsp5_setup_input_attention, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_mt_probe(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct cyttsp5_mt_platform_data *mt_pdata;
|
||||
int rc = 0;
|
||||
|
||||
if (!pdata || !pdata->mt_pdata) {
|
||||
dev_err(dev, "%s: Missing platform data\n", __func__);
|
||||
rc = -ENODEV;
|
||||
goto error_no_pdata;
|
||||
}
|
||||
mt_pdata = pdata->mt_pdata;
|
||||
|
||||
cyttsp5_init_function_ptrs(md);
|
||||
|
||||
mutex_init(&md->mt_lock);
|
||||
md->dev = dev;
|
||||
md->pdata = mt_pdata;
|
||||
|
||||
/* Create the input device and register it. */
|
||||
dev_vdbg(dev, "%s: Create the input device and register it\n",
|
||||
__func__);
|
||||
md->input = input_allocate_device();
|
||||
if (!md->input) {
|
||||
dev_err(dev, "%s: Error, failed to allocate input device\n",
|
||||
__func__);
|
||||
rc = -ENODEV;
|
||||
goto error_alloc_failed;
|
||||
}
|
||||
|
||||
if (md->pdata->inp_dev_name)
|
||||
md->input->name = md->pdata->inp_dev_name;
|
||||
else
|
||||
md->input->name = CYTTSP5_MT_NAME;
|
||||
scnprintf(md->phys, sizeof(md->phys), "%s/input%d", dev_name(dev),
|
||||
cd->phys_num++);
|
||||
md->input->phys = md->phys;
|
||||
md->input->dev.parent = md->dev;
|
||||
md->input->open = cyttsp5_mt_open;
|
||||
md->input->close = cyttsp5_mt_close;
|
||||
input_set_drvdata(md->input, md);
|
||||
|
||||
/* get sysinfo */
|
||||
md->si = _cyttsp5_request_sysinfo(dev);
|
||||
|
||||
if (md->si) {
|
||||
rc = cyttsp5_setup_input_device(dev);
|
||||
if (rc)
|
||||
goto error_init_input;
|
||||
} else {
|
||||
dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
|
||||
__func__, md->si);
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_MT_NAME, cyttsp5_setup_input_attention, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_init_input:
|
||||
input_free_device(md->input);
|
||||
error_alloc_failed:
|
||||
error_no_pdata:
|
||||
dev_err(dev, "%s failed.\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_mt_release(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_mt_data *md = &cd->md;
|
||||
|
||||
if (md->input_device_registered) {
|
||||
input_unregister_device(md->input);
|
||||
} else {
|
||||
input_free_device(md->input);
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_MT_NAME, cyttsp5_setup_input_attention, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* cyttsp5_mta.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Multi-Touch Protocol A Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
static void cyttsp5_final_sync(struct input_dev *input, int max_slots,
|
||||
int mt_sync_count, unsigned long *ids)
|
||||
{
|
||||
if (mt_sync_count)
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static void cyttsp5_input_sync(struct input_dev *input)
|
||||
{
|
||||
input_mt_sync(input);
|
||||
}
|
||||
|
||||
static void cyttsp5_input_report(struct input_dev *input, int sig,
|
||||
int t, int type)
|
||||
{
|
||||
if (type == CY_OBJ_STANDARD_FINGER || type == CY_OBJ_GLOVE
|
||||
|| type == CY_OBJ_HOVER) {
|
||||
input_report_key(input, BTN_TOOL_FINGER, CY_BTN_PRESSED);
|
||||
input_report_key(input, BTN_TOOL_PEN, CY_BTN_RELEASED);
|
||||
} else if (type == CY_OBJ_STYLUS) {
|
||||
input_report_key(input, BTN_TOOL_PEN, CY_BTN_PRESSED);
|
||||
input_report_key(input, BTN_TOOL_FINGER, CY_BTN_RELEASED);
|
||||
}
|
||||
|
||||
if (type != CY_OBJ_HOVER)
|
||||
input_report_key(input, BTN_TOUCH, CY_BTN_PRESSED);
|
||||
|
||||
input_report_abs(input, sig, t);
|
||||
}
|
||||
|
||||
static void cyttsp5_report_slot_liftoff(struct cyttsp5_mt_data *md,
|
||||
int max_slots)
|
||||
{
|
||||
input_report_key(md->input, BTN_TOUCH, CY_BTN_RELEASED);
|
||||
input_report_key(md->input, BTN_TOOL_FINGER, CY_BTN_RELEASED);
|
||||
input_report_key(md->input, BTN_TOOL_PEN, CY_BTN_RELEASED);
|
||||
|
||||
}
|
||||
|
||||
static int cyttsp5_input_register_device(struct input_dev *input, int max_slots)
|
||||
{
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input->keybit);
|
||||
return input_register_device(input);
|
||||
}
|
||||
|
||||
void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md)
|
||||
{
|
||||
md->mt_function.report_slot_liftoff = cyttsp5_report_slot_liftoff;
|
||||
md->mt_function.final_sync = cyttsp5_final_sync;
|
||||
md->mt_function.input_sync = cyttsp5_input_sync;
|
||||
md->mt_function.input_report = cyttsp5_input_report;
|
||||
md->mt_function.input_register_device = cyttsp5_input_register_device;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* cyttsp5_mtb.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Multi-Touch Protocol B Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
static void cyttsp5_final_sync(struct input_dev *input, int max_slots,
|
||||
int mt_sync_count, unsigned long *ids)
|
||||
{
|
||||
int t;
|
||||
|
||||
for (t = 0; t < max_slots; t++) {
|
||||
if (test_bit(t, ids))
|
||||
continue;
|
||||
input_mt_slot(input, t);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static void cyttsp5_input_report(struct input_dev *input, int sig,
|
||||
int t, int type)
|
||||
{
|
||||
input_mt_slot(input, t);
|
||||
|
||||
if (type == CY_OBJ_STANDARD_FINGER || type == CY_OBJ_GLOVE
|
||||
|| type == CY_OBJ_HOVER)
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
|
||||
else if (type == CY_OBJ_STYLUS)
|
||||
input_mt_report_slot_state(input, MT_TOOL_PEN, true);
|
||||
}
|
||||
|
||||
static void cyttsp5_report_slot_liftoff(struct cyttsp5_mt_data *md,
|
||||
int max_slots)
|
||||
{
|
||||
int t;
|
||||
|
||||
if (md->num_prv_rec == 0)
|
||||
return;
|
||||
|
||||
for (t = 0; t < max_slots; t++) {
|
||||
input_mt_slot(md->input, t);
|
||||
input_mt_report_slot_state(md->input,
|
||||
MT_TOOL_FINGER, false);
|
||||
}
|
||||
}
|
||||
|
||||
static int cyttsp5_input_register_device(struct input_dev *input, int max_slots)
|
||||
{
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
|
||||
input_mt_init_slots(input, max_slots, 0);
|
||||
#else
|
||||
input_mt_init_slots(input, max_slots);
|
||||
#endif
|
||||
return input_register_device(input);
|
||||
}
|
||||
|
||||
void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md)
|
||||
{
|
||||
md->mt_function.report_slot_liftoff = cyttsp5_report_slot_liftoff;
|
||||
md->mt_function.final_sync = cyttsp5_final_sync;
|
||||
md->mt_function.input_sync = NULL;
|
||||
md->mt_function.input_report = cyttsp5_input_report;
|
||||
md->mt_function.input_register_device = cyttsp5_input_register_device;
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* cyttsp5_platform.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Platform Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2013-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
#include <linux/platform_data/cyttsp5.h>
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
|
||||
/* FW for Panel ID = 0x00 */
|
||||
#include "cyttsp5_fw_pid00.h"
|
||||
static struct cyttsp5_touch_firmware cyttsp5_firmware_pid00 = {
|
||||
.img = cyttsp4_img_pid00,
|
||||
.size = ARRAY_SIZE(cyttsp4_img_pid00),
|
||||
.ver = cyttsp4_ver_pid00,
|
||||
.vsize = ARRAY_SIZE(cyttsp4_ver_pid00),
|
||||
.panel_id = 0x00,
|
||||
};
|
||||
|
||||
/* FW for Panel ID = 0x01 */
|
||||
#include "cyttsp5_fw_pid01.h"
|
||||
static struct cyttsp5_touch_firmware cyttsp5_firmware_pid01 = {
|
||||
.img = cyttsp4_img_pid01,
|
||||
.size = ARRAY_SIZE(cyttsp4_img_pid01),
|
||||
.ver = cyttsp4_ver_pid01,
|
||||
.vsize = ARRAY_SIZE(cyttsp4_ver_pid01),
|
||||
.panel_id = 0x01,
|
||||
};
|
||||
|
||||
/* FW for Panel ID not enabled (legacy) */
|
||||
#include "cyttsp5_fw.h"
|
||||
static struct cyttsp5_touch_firmware cyttsp5_firmware = {
|
||||
.img = cyttsp4_img,
|
||||
.size = ARRAY_SIZE(cyttsp4_img),
|
||||
.ver = cyttsp4_ver,
|
||||
.vsize = ARRAY_SIZE(cyttsp4_ver),
|
||||
};
|
||||
#else
|
||||
/* FW for Panel ID not enabled (legacy) */
|
||||
static struct cyttsp5_touch_firmware cyttsp5_firmware = {
|
||||
.img = NULL,
|
||||
.size = 0,
|
||||
.ver = NULL,
|
||||
.vsize = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
|
||||
/* TT Config for Panel ID = 0x00 */
|
||||
#include "cyttsp5_params_pid00.h"
|
||||
static struct touch_settings cyttsp5_sett_param_regs_pid00 = {
|
||||
.data = (uint8_t *)&cyttsp4_param_regs_pid00[0],
|
||||
.size = ARRAY_SIZE(cyttsp4_param_regs_pid00),
|
||||
.tag = 0,
|
||||
};
|
||||
|
||||
static struct touch_settings cyttsp5_sett_param_size_pid00 = {
|
||||
.data = (uint8_t *)&cyttsp4_param_size_pid00[0],
|
||||
.size = ARRAY_SIZE(cyttsp4_param_size_pid00),
|
||||
.tag = 0,
|
||||
};
|
||||
|
||||
static struct cyttsp5_touch_config cyttsp5_ttconfig_pid00 = {
|
||||
.param_regs = &cyttsp5_sett_param_regs_pid00,
|
||||
.param_size = &cyttsp5_sett_param_size_pid00,
|
||||
.fw_ver = ttconfig_fw_ver_pid00,
|
||||
.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid00),
|
||||
.panel_id = 0x00,
|
||||
};
|
||||
|
||||
/* TT Config for Panel ID = 0x01 */
|
||||
#include "cyttsp5_params_pid01.h"
|
||||
static struct touch_settings cyttsp5_sett_param_regs_pid01 = {
|
||||
.data = (uint8_t *)&cyttsp4_param_regs_pid01[0],
|
||||
.size = ARRAY_SIZE(cyttsp4_param_regs_pid01),
|
||||
.tag = 0,
|
||||
};
|
||||
|
||||
static struct touch_settings cyttsp5_sett_param_size_pid01 = {
|
||||
.data = (uint8_t *)&cyttsp4_param_size_pid01[0],
|
||||
.size = ARRAY_SIZE(cyttsp4_param_size_pid01),
|
||||
.tag = 0,
|
||||
};
|
||||
|
||||
static struct cyttsp5_touch_config cyttsp5_ttconfig_pid01 = {
|
||||
.param_regs = &cyttsp5_sett_param_regs_pid01,
|
||||
.param_size = &cyttsp5_sett_param_size_pid01,
|
||||
.fw_ver = ttconfig_fw_ver_pid01,
|
||||
.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid01),
|
||||
.panel_id = 0x01,
|
||||
};
|
||||
|
||||
/* TT Config for Panel ID not enabled (legacy)*/
|
||||
#include "cyttsp5_params.h"
|
||||
static struct touch_settings cyttsp5_sett_param_regs = {
|
||||
.data = (uint8_t *)&cyttsp4_param_regs[0],
|
||||
.size = ARRAY_SIZE(cyttsp4_param_regs),
|
||||
.tag = 0,
|
||||
};
|
||||
|
||||
static struct touch_settings cyttsp5_sett_param_size = {
|
||||
.data = (uint8_t *)&cyttsp4_param_size[0],
|
||||
.size = ARRAY_SIZE(cyttsp4_param_size),
|
||||
.tag = 0,
|
||||
};
|
||||
|
||||
static struct cyttsp5_touch_config cyttsp5_ttconfig = {
|
||||
.param_regs = &cyttsp5_sett_param_regs,
|
||||
.param_size = &cyttsp5_sett_param_size,
|
||||
.fw_ver = ttconfig_fw_ver,
|
||||
.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver),
|
||||
};
|
||||
#else
|
||||
/* TT Config for Panel ID not enabled (legacy)*/
|
||||
static struct cyttsp5_touch_config cyttsp5_ttconfig = {
|
||||
.param_regs = NULL,
|
||||
.param_size = NULL,
|
||||
.fw_ver = NULL,
|
||||
.fw_vsize = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct cyttsp5_touch_firmware *cyttsp5_firmwares[] = {
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
|
||||
&cyttsp5_firmware_pid00,
|
||||
&cyttsp5_firmware_pid01,
|
||||
#endif
|
||||
NULL, /* Last item should always be NULL */
|
||||
};
|
||||
|
||||
static struct cyttsp5_touch_config *cyttsp5_ttconfigs[] = {
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
|
||||
&cyttsp5_ttconfig_pid00,
|
||||
&cyttsp5_ttconfig_pid01,
|
||||
#endif
|
||||
NULL, /* Last item should always be NULL */
|
||||
};
|
||||
|
||||
struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data = {
|
||||
.fw = &cyttsp5_firmware,
|
||||
.ttconfig = &cyttsp5_ttconfig,
|
||||
.fws = cyttsp5_firmwares,
|
||||
.ttconfigs = cyttsp5_ttconfigs,
|
||||
.flags = CY_LOADER_FLAG_NONE,
|
||||
};
|
||||
|
||||
int cyttsp5_xres(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev)
|
||||
{
|
||||
int rst_gpio = pdata->rst_gpio;
|
||||
int rc = 0;
|
||||
|
||||
gpio_set_value(rst_gpio, 1);
|
||||
msleep(20);
|
||||
gpio_set_value(rst_gpio, 0);
|
||||
msleep(40);
|
||||
gpio_set_value(rst_gpio, 1);
|
||||
msleep(20);
|
||||
dev_info(dev,
|
||||
"%s: RESET CYTTSP gpio=%d r=%d\n", __func__,
|
||||
pdata->rst_gpio, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_init(struct cyttsp5_core_platform_data *pdata,
|
||||
int on, struct device *dev)
|
||||
{
|
||||
int rst_gpio = pdata->rst_gpio;
|
||||
int irq_gpio = pdata->irq_gpio;
|
||||
int rc = 0;
|
||||
|
||||
if (on) {
|
||||
rc = gpio_request(rst_gpio, NULL);
|
||||
if (rc < 0) {
|
||||
gpio_free(rst_gpio);
|
||||
rc = gpio_request(rst_gpio, NULL);
|
||||
}
|
||||
if (rc < 0) {
|
||||
dev_err(dev,
|
||||
"%s: Fail request gpio=%d\n", __func__,
|
||||
rst_gpio);
|
||||
} else {
|
||||
rc = gpio_direction_output(rst_gpio, 1);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Fail set output gpio=%d\n",
|
||||
__func__, rst_gpio);
|
||||
gpio_free(rst_gpio);
|
||||
} else {
|
||||
rc = gpio_request(irq_gpio, NULL);
|
||||
if (rc < 0) {
|
||||
gpio_free(irq_gpio);
|
||||
rc = gpio_request(irq_gpio,
|
||||
NULL);
|
||||
}
|
||||
if (rc < 0) {
|
||||
dev_err(dev,
|
||||
"%s: Fail request gpio=%d\n",
|
||||
__func__, irq_gpio);
|
||||
gpio_free(rst_gpio);
|
||||
} else {
|
||||
gpio_direction_input(irq_gpio);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gpio_free(rst_gpio);
|
||||
gpio_free(irq_gpio);
|
||||
}
|
||||
|
||||
dev_info(dev, "%s: INIT CYTTSP RST gpio=%d and IRQ gpio=%d r=%d\n",
|
||||
__func__, rst_gpio, irq_gpio, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_wakeup(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev, atomic_t *ignore_irq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_sleep(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev, atomic_t *ignore_irq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cyttsp5_power(struct cyttsp5_core_platform_data *pdata,
|
||||
int on, struct device *dev, atomic_t *ignore_irq)
|
||||
{
|
||||
if (on)
|
||||
return cyttsp5_wakeup(pdata, dev, ignore_irq);
|
||||
|
||||
return cyttsp5_sleep(pdata, dev, ignore_irq);
|
||||
}
|
||||
|
||||
int cyttsp5_irq_stat(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev)
|
||||
{
|
||||
return gpio_get_value(pdata->irq_gpio);
|
||||
}
|
||||
|
||||
#ifdef CYTTSP5_DETECT_HW
|
||||
int cyttsp5_detect(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev, cyttsp5_platform_read read)
|
||||
{
|
||||
int retry = 3;
|
||||
int rc;
|
||||
char buf[1];
|
||||
|
||||
while (retry--) {
|
||||
/* Perform reset, wait for 100 ms and perform read */
|
||||
dev_vdbg(dev, "%s: Performing a reset\n", __func__);
|
||||
pdata->xres(pdata, dev);
|
||||
msleep(100);
|
||||
rc = read(dev, buf, 1);
|
||||
if (!rc)
|
||||
return 0;
|
||||
|
||||
dev_vdbg(dev, "%s: Read unsuccessful, try=%d\n",
|
||||
__func__, 3 - retry);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* cyttsp5_proximity.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Proximity Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2013-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
#define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity"
|
||||
|
||||
/* Timeout value in ms. */
|
||||
#define CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT 1000
|
||||
|
||||
#define CYTTSP5_PROXIMITY_ON 0
|
||||
#define CYTTSP5_PROXIMITY_OFF 1
|
||||
|
||||
static inline struct cyttsp5_proximity_data *get_prox_data(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
|
||||
return &cd->pd;
|
||||
}
|
||||
|
||||
static void cyttsp5_report_proximity(struct cyttsp5_proximity_data *pd,
|
||||
bool on)
|
||||
{
|
||||
int val = on ? CYTTSP5_PROXIMITY_ON : CYTTSP5_PROXIMITY_OFF;
|
||||
|
||||
input_report_abs(pd->input, ABS_DISTANCE, val);
|
||||
input_sync(pd->input);
|
||||
}
|
||||
|
||||
static void cyttsp5_get_touch_axis(struct cyttsp5_proximity_data *pd,
|
||||
int *axis, int size, int max, u8 *xy_data, int bofs)
|
||||
{
|
||||
int nbyte;
|
||||
int next;
|
||||
|
||||
for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
|
||||
dev_vdbg(pd->dev,
|
||||
"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
|
||||
__func__, *axis, *axis, size, max, xy_data, next,
|
||||
xy_data[next], xy_data[next], bofs);
|
||||
*axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
|
||||
next++;
|
||||
}
|
||||
|
||||
*axis &= max - 1;
|
||||
|
||||
dev_vdbg(pd->dev,
|
||||
"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
|
||||
__func__, *axis, *axis, size, max, xy_data, next,
|
||||
xy_data[next], xy_data[next]);
|
||||
}
|
||||
|
||||
static void cyttsp5_get_touch_hdr(struct cyttsp5_proximity_data *pd,
|
||||
struct cyttsp5_touch *touch, u8 *xy_mode)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
struct cyttsp5_sysinfo *si = pd->si;
|
||||
enum cyttsp5_tch_hdr hdr;
|
||||
|
||||
for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) {
|
||||
if (!si->tch_hdr[hdr].report)
|
||||
continue;
|
||||
cyttsp5_get_touch_axis(pd, &touch->hdr[hdr],
|
||||
si->tch_hdr[hdr].size,
|
||||
si->tch_hdr[hdr].max,
|
||||
xy_mode + si->tch_hdr[hdr].ofs,
|
||||
si->tch_hdr[hdr].bofs);
|
||||
dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
|
||||
cyttsp5_tch_hdr_string[hdr],
|
||||
touch->hdr[hdr], touch->hdr[hdr]);
|
||||
}
|
||||
}
|
||||
|
||||
static void cyttsp5_get_touch(struct cyttsp5_proximity_data *pd,
|
||||
struct cyttsp5_touch *touch, u8 *xy_data)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
struct cyttsp5_sysinfo *si = pd->si;
|
||||
enum cyttsp5_tch_abs abs;
|
||||
|
||||
for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
|
||||
if (!si->tch_abs[abs].report)
|
||||
continue;
|
||||
cyttsp5_get_touch_axis(pd, &touch->abs[abs],
|
||||
si->tch_abs[abs].size,
|
||||
si->tch_abs[abs].max,
|
||||
xy_data + si->tch_abs[abs].ofs,
|
||||
si->tch_abs[abs].bofs);
|
||||
dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
|
||||
cyttsp5_tch_abs_string[abs],
|
||||
touch->abs[abs], touch->abs[abs]);
|
||||
}
|
||||
|
||||
dev_vdbg(dev, "%s: x=%04X(%d) y=%04X(%d)\n", __func__,
|
||||
touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
|
||||
touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
|
||||
}
|
||||
|
||||
static void cyttsp5_get_proximity_touch(struct cyttsp5_proximity_data *pd,
|
||||
struct cyttsp5_touch *tch, int num_cur_tch)
|
||||
{
|
||||
struct cyttsp5_sysinfo *si = pd->si;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_cur_tch; i++) {
|
||||
cyttsp5_get_touch(pd, tch, si->xy_data +
|
||||
(i * si->desc.tch_record_size));
|
||||
|
||||
/* Check for proximity event */
|
||||
if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) {
|
||||
if (tch->abs[CY_TCH_E] == CY_EV_TOUCHDOWN)
|
||||
cyttsp5_report_proximity(pd, true);
|
||||
else if (tch->abs[CY_TCH_E] == CY_EV_LIFTOFF)
|
||||
cyttsp5_report_proximity(pd, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* read xy_data for all current touches */
|
||||
static int cyttsp5_xy_worker(struct cyttsp5_proximity_data *pd)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
struct cyttsp5_sysinfo *si = pd->si;
|
||||
struct cyttsp5_touch tch;
|
||||
u8 num_cur_tch;
|
||||
|
||||
cyttsp5_get_touch_hdr(pd, &tch, si->xy_mode + 3);
|
||||
|
||||
num_cur_tch = tch.hdr[CY_TCH_NUM];
|
||||
if (num_cur_tch > si->sensing_conf_data.max_tch) {
|
||||
dev_err(dev, "%s: Num touch err detected (n=%d)\n",
|
||||
__func__, num_cur_tch);
|
||||
num_cur_tch = si->sensing_conf_data.max_tch;
|
||||
}
|
||||
|
||||
if (tch.hdr[CY_TCH_LO])
|
||||
dev_dbg(dev, "%s: Large area detected\n", __func__);
|
||||
|
||||
/* extract xy_data for all currently reported touches */
|
||||
dev_vdbg(dev, "%s: extract data num_cur_rec=%d\n", __func__,
|
||||
num_cur_tch);
|
||||
if (num_cur_tch)
|
||||
cyttsp5_get_proximity_touch(pd, &tch, num_cur_tch);
|
||||
else
|
||||
cyttsp5_report_proximity(pd, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp5_proximity_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
int rc = 0;
|
||||
|
||||
if (pd->si->xy_mode[2] != pd->si->desc.tch_report_id)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&pd->prox_lock);
|
||||
rc = cyttsp5_xy_worker(pd);
|
||||
mutex_unlock(&pd->prox_lock);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_startup_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
|
||||
mutex_lock(&pd->prox_lock);
|
||||
cyttsp5_report_proximity(pd, false);
|
||||
mutex_unlock(&pd->prox_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cyttsp5_set_proximity_via_touchmode_enabled(
|
||||
struct cyttsp5_proximity_data *pd, bool enable)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
u32 touchmode_enabled;
|
||||
int rc;
|
||||
|
||||
rc = cyttsp5_request_nonhid_get_param(dev, 0,
|
||||
CY_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (enable)
|
||||
touchmode_enabled |= 0x80;
|
||||
else
|
||||
touchmode_enabled &= 0x7F;
|
||||
|
||||
rc = cyttsp5_request_nonhid_set_param(dev, 0,
|
||||
CY_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled,
|
||||
CY_RAM_ID_TOUCHMODE_ENABLED_SIZE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _cyttsp5_set_proximity_via_proximity_enable(
|
||||
struct cyttsp5_proximity_data *pd, bool enable)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
u32 proximity_enable;
|
||||
int rc;
|
||||
|
||||
rc = cyttsp5_request_nonhid_get_param(dev, 0,
|
||||
CY_RAM_ID_PROXIMITY_ENABLE, &proximity_enable);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (enable)
|
||||
proximity_enable |= 0x01;
|
||||
else
|
||||
proximity_enable &= 0xFE;
|
||||
|
||||
rc = cyttsp5_request_nonhid_set_param(dev, 0,
|
||||
CY_RAM_ID_PROXIMITY_ENABLE, proximity_enable,
|
||||
CY_RAM_ID_PROXIMITY_ENABLE_SIZE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _cyttsp5_set_proximity(struct cyttsp5_proximity_data *pd,
|
||||
bool enable)
|
||||
{
|
||||
if (!IS_PIP_VER_GE(pd->si, 1, 4))
|
||||
return _cyttsp5_set_proximity_via_touchmode_enabled(pd,
|
||||
enable);
|
||||
|
||||
return _cyttsp5_set_proximity_via_proximity_enable(pd, enable);
|
||||
}
|
||||
|
||||
static int _cyttsp5_proximity_enable(struct cyttsp5_proximity_data *pd)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
int rc = 0;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
rc = cyttsp5_request_exclusive(dev,
|
||||
CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Error on request exclusive r=%d\n",
|
||||
__func__, rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _cyttsp5_set_proximity(pd, true);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Error on request enable proximity scantype r=%d\n",
|
||||
__func__, rc);
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
dev_vdbg(dev, "%s: setup subscriptions\n", __func__);
|
||||
|
||||
/* set up touch call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_PROXIMITY_NAME,
|
||||
cyttsp5_proximity_attention, CY_MODE_OPERATIONAL);
|
||||
|
||||
/* set up startup call back */
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0);
|
||||
|
||||
exit_release:
|
||||
cyttsp5_release_exclusive(dev);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _cyttsp5_proximity_disable(struct cyttsp5_proximity_data *pd,
|
||||
bool force)
|
||||
{
|
||||
struct device *dev = pd->dev;
|
||||
int rc = 0;
|
||||
|
||||
rc = cyttsp5_request_exclusive(dev,
|
||||
CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Error on request exclusive r=%d\n",
|
||||
__func__, rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _cyttsp5_set_proximity(pd, false);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: Error on request disable proximity scan r=%d\n",
|
||||
__func__, rc);
|
||||
goto exit_release;
|
||||
}
|
||||
|
||||
exit_release:
|
||||
cyttsp5_release_exclusive(dev);
|
||||
|
||||
exit:
|
||||
if (!rc || force) {
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ,
|
||||
CYTTSP5_PROXIMITY_NAME, cyttsp5_proximity_attention,
|
||||
CY_MODE_OPERATIONAL);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0);
|
||||
}
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t cyttsp5_proximity_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
int val = 0;
|
||||
|
||||
mutex_lock(&pd->sysfs_lock);
|
||||
val = pd->enable_count;
|
||||
mutex_unlock(&pd->sysfs_lock);
|
||||
|
||||
return scnprintf(buf, CY_MAX_PRBUF_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t cyttsp5_proximity_enable_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
unsigned long value;
|
||||
int rc;
|
||||
|
||||
rc = kstrtoul(buf, 10, &value);
|
||||
if (rc < 0 || (value != 0 && value != 1)) {
|
||||
dev_err(dev, "%s: Invalid value\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&pd->sysfs_lock);
|
||||
if (value) {
|
||||
if (pd->enable_count++) {
|
||||
dev_vdbg(dev, "%s: '%s' already enabled\n", __func__,
|
||||
pd->input->name);
|
||||
} else {
|
||||
rc = _cyttsp5_proximity_enable(pd);
|
||||
if (rc)
|
||||
pd->enable_count--;
|
||||
}
|
||||
} else {
|
||||
if (--pd->enable_count) {
|
||||
if (pd->enable_count < 0) {
|
||||
dev_err(dev, "%s: '%s' unbalanced disable\n",
|
||||
__func__, pd->input->name);
|
||||
pd->enable_count = 0;
|
||||
}
|
||||
} else {
|
||||
rc = _cyttsp5_proximity_disable(pd, false);
|
||||
if (rc)
|
||||
pd->enable_count++;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&pd->sysfs_lock);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(prox_enable, S_IRUSR | S_IWUSR,
|
||||
cyttsp5_proximity_enable_show,
|
||||
cyttsp5_proximity_enable_store);
|
||||
|
||||
static int cyttsp5_setup_input_device_and_sysfs(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
int signal = CY_IGNORE_VALUE;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
rc = device_create_file(dev, &dev_attr_prox_enable);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s: Error, could not create enable\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
|
||||
|
||||
__set_bit(EV_ABS, pd->input->evbit);
|
||||
|
||||
/* set event signal capabilities */
|
||||
for (i = 0; i < NUM_SIGNALS(pd->pdata->frmwrk); i++) {
|
||||
signal = PARAM_SIGNAL(pd->pdata->frmwrk, i);
|
||||
if (signal != CY_IGNORE_VALUE) {
|
||||
input_set_abs_params(pd->input, signal,
|
||||
PARAM_MIN(pd->pdata->frmwrk, i),
|
||||
PARAM_MAX(pd->pdata->frmwrk, i),
|
||||
PARAM_FUZZ(pd->pdata->frmwrk, i),
|
||||
PARAM_FLAT(pd->pdata->frmwrk, i));
|
||||
}
|
||||
}
|
||||
|
||||
rc = input_register_device(pd->input);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s: Error, failed register input device r=%d\n",
|
||||
__func__, rc);
|
||||
goto unregister_enable;
|
||||
}
|
||||
|
||||
pd->input_device_registered = true;
|
||||
return rc;
|
||||
|
||||
unregister_enable:
|
||||
device_remove_file(dev, &dev_attr_prox_enable);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_setup_input_attention(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
int rc;
|
||||
|
||||
pd->si = _cyttsp5_request_sysinfo(dev);
|
||||
if (!pd->si)
|
||||
return -EINVAL;
|
||||
|
||||
rc = cyttsp5_setup_input_device_and_sysfs(dev);
|
||||
if (!rc)
|
||||
rc = _cyttsp5_set_proximity(pd, false);
|
||||
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_proximity_probe(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
|
||||
struct cyttsp5_proximity_data *pd = &cd->pd;
|
||||
struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct cyttsp5_proximity_platform_data *prox_pdata;
|
||||
int rc = 0;
|
||||
|
||||
if (!pdata || !pdata->prox_pdata) {
|
||||
dev_err(dev, "%s: Missing platform data\n", __func__);
|
||||
rc = -ENODEV;
|
||||
goto error_no_pdata;
|
||||
}
|
||||
prox_pdata = pdata->prox_pdata;
|
||||
|
||||
mutex_init(&pd->prox_lock);
|
||||
mutex_init(&pd->sysfs_lock);
|
||||
pd->dev = dev;
|
||||
pd->pdata = prox_pdata;
|
||||
|
||||
/* Create the input device and register it. */
|
||||
dev_vdbg(dev, "%s: Create the input device and register it\n",
|
||||
__func__);
|
||||
pd->input = input_allocate_device();
|
||||
if (!pd->input) {
|
||||
dev_err(dev, "%s: Error, failed to allocate input device\n",
|
||||
__func__);
|
||||
rc = -ENODEV;
|
||||
goto error_alloc_failed;
|
||||
}
|
||||
|
||||
if (pd->pdata->inp_dev_name)
|
||||
pd->input->name = pd->pdata->inp_dev_name;
|
||||
else
|
||||
pd->input->name = CYTTSP5_PROXIMITY_NAME;
|
||||
scnprintf(pd->phys, sizeof(pd->phys), "%s/input%d", dev_name(dev),
|
||||
cd->phys_num++);
|
||||
pd->input->phys = pd->phys;
|
||||
pd->input->dev.parent = pd->dev;
|
||||
input_set_drvdata(pd->input, pd);
|
||||
|
||||
/* get sysinfo */
|
||||
pd->si = _cyttsp5_request_sysinfo(dev);
|
||||
|
||||
if (pd->si) {
|
||||
rc = cyttsp5_setup_input_device_and_sysfs(dev);
|
||||
if (rc)
|
||||
goto error_init_input;
|
||||
|
||||
rc = _cyttsp5_set_proximity(pd, false);
|
||||
} else {
|
||||
dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
|
||||
__func__, pd->si);
|
||||
_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention,
|
||||
0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_init_input:
|
||||
input_free_device(pd->input);
|
||||
error_alloc_failed:
|
||||
error_no_pdata:
|
||||
dev_err(dev, "%s failed.\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cyttsp5_proximity_release(struct device *dev)
|
||||
{
|
||||
struct cyttsp5_proximity_data *pd = get_prox_data(dev);
|
||||
|
||||
if (pd->input_device_registered) {
|
||||
/* Disable proximity sensing */
|
||||
mutex_lock(&pd->sysfs_lock);
|
||||
if (pd->enable_count)
|
||||
_cyttsp5_proximity_disable(pd, true);
|
||||
mutex_unlock(&pd->sysfs_lock);
|
||||
device_remove_file(dev, &dev_attr_prox_enable);
|
||||
input_unregister_device(pd->input);
|
||||
} else {
|
||||
input_free_device(pd->input);
|
||||
_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
|
||||
CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention,
|
||||
0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* cyttsp5_spi.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 SPI Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cyttsp5_regs.h"
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define CY_SPI_WR_OP 0x00 /* r/~w */
|
||||
#define CY_SPI_RD_OP 0x01
|
||||
#define CY_SPI_BITS_PER_WORD 8
|
||||
#define CY_SPI_SYNC_ACK 0x62
|
||||
|
||||
#define CY_SPI_CMD_BYTES 0
|
||||
#define CY_SPI_DATA_SIZE (2 * 256)
|
||||
#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
|
||||
|
||||
static void cyttsp5_spi_add_rw_msg(struct spi_message *msg,
|
||||
struct spi_transfer *xfer, u8 *w_header, u8 *r_header, u8 op)
|
||||
{
|
||||
xfer->tx_buf = w_header;
|
||||
xfer->rx_buf = r_header;
|
||||
w_header[0] = op;
|
||||
xfer->len = 1;
|
||||
spi_message_add_tail(xfer, msg);
|
||||
}
|
||||
|
||||
static int cyttsp5_spi_xfer(struct device *dev, u8 op, u8 *buf, int length)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[2];
|
||||
u8 w_header[2];
|
||||
u8 r_header[2];
|
||||
int rc;
|
||||
|
||||
memset(xfer, 0, sizeof(xfer));
|
||||
|
||||
spi_message_init(&msg);
|
||||
cyttsp5_spi_add_rw_msg(&msg, &xfer[0], w_header, r_header, op);
|
||||
|
||||
switch (op) {
|
||||
case CY_SPI_RD_OP:
|
||||
xfer[1].rx_buf = buf;
|
||||
xfer[1].len = length;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
break;
|
||||
case CY_SPI_WR_OP:
|
||||
xfer[1].tx_buf = buf;
|
||||
xfer[1].len = length;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
break;
|
||||
default:
|
||||
rc = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = spi_sync(spi, &msg);
|
||||
exit:
|
||||
if (rc < 0)
|
||||
dev_vdbg(dev, "%s: spi_sync() error %d\n", __func__, rc);
|
||||
|
||||
if (r_header[0] != CY_SPI_SYNC_ACK)
|
||||
return -EIO;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_spi_read_default(struct device *dev, void *buf, int size)
|
||||
{
|
||||
if (!buf || !size)
|
||||
return 0;
|
||||
|
||||
return cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, size);
|
||||
}
|
||||
|
||||
static int cyttsp5_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max)
|
||||
{
|
||||
u32 size;
|
||||
int rc;
|
||||
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
rc = cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, 2);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
size = get_unaligned_le16(&buf[0]);
|
||||
if (!size)
|
||||
return rc;
|
||||
|
||||
if (size > max)
|
||||
return -EINVAL;
|
||||
|
||||
return cyttsp5_spi_read_default(dev, buf, size);
|
||||
}
|
||||
|
||||
static int cyttsp5_spi_write_read_specific(struct device *dev, u8 write_len,
|
||||
u8 *write_buf, u8 *read_buf)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = cyttsp5_spi_xfer(dev, CY_SPI_WR_OP, write_buf, write_len);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (read_buf)
|
||||
rc = cyttsp5_spi_read_default_nosize(dev, read_buf,
|
||||
CY_SPI_DATA_SIZE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct cyttsp5_bus_ops cyttsp5_spi_bus_ops = {
|
||||
.bustype = BUS_SPI,
|
||||
.read_default = cyttsp5_spi_read_default,
|
||||
.read_default_nosize = cyttsp5_spi_read_default_nosize,
|
||||
.write_read_specific = cyttsp5_spi_write_read_specific,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
static const struct of_device_id cyttsp5_spi_of_match[] = {
|
||||
{ .compatible = "cy,cyttsp5_spi_adapter", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cyttsp5_spi_of_match);
|
||||
#endif
|
||||
|
||||
static int cyttsp5_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
const struct of_device_id *match;
|
||||
#endif
|
||||
int rc;
|
||||
|
||||
/* Set up SPI*/
|
||||
spi->bits_per_word = CY_SPI_BITS_PER_WORD;
|
||||
spi->mode = SPI_MODE_0;
|
||||
rc = spi_setup(spi);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: SPI setup error %d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev);
|
||||
if (match) {
|
||||
rc = cyttsp5_devtree_create_and_get_pdata(dev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = cyttsp5_probe(&cyttsp5_spi_bus_ops, &spi->dev, spi->irq,
|
||||
CY_SPI_DATA_BUF_SIZE);
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
if (rc && match)
|
||||
cyttsp5_devtree_clean_pdata(dev);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cyttsp5_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
struct device *dev = &spi->dev;
|
||||
const struct of_device_id *match;
|
||||
#endif
|
||||
struct cyttsp5_core_data *cd = dev_get_drvdata(&spi->dev);
|
||||
|
||||
cyttsp5_release(cd);
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev);
|
||||
if (match)
|
||||
cyttsp5_devtree_clean_pdata(dev);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id cyttsp5_spi_id[] = {
|
||||
{ CYTTSP5_SPI_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, cyttsp5_spi_id);
|
||||
|
||||
static struct spi_driver cyttsp5_spi_driver = {
|
||||
.driver = {
|
||||
.name = CYTTSP5_SPI_NAME,
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyttsp5_pm_ops,
|
||||
#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
|
||||
.of_match_table = cyttsp5_spi_of_match,
|
||||
#endif
|
||||
},
|
||||
.probe = cyttsp5_spi_probe,
|
||||
.remove = (cyttsp5_spi_remove),
|
||||
.id_table = cyttsp5_spi_id,
|
||||
};
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
|
||||
module_spi_driver(cyttsp5_spi_driver);
|
||||
#else
|
||||
static int __init cyttsp5_spi_init(void)
|
||||
{
|
||||
int err = spi_register_driver(&cyttsp5_spi_driver);
|
||||
|
||||
pr_info("%s: Parade TTSP SPI Driver (Built %s) rc=%d\n",
|
||||
__func__, CY_DRIVER_VERSION, err);
|
||||
return err;
|
||||
}
|
||||
module_init(cyttsp5_spi_init);
|
||||
|
||||
static void __exit cyttsp5_spi_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&cyttsp5_spi_driver);
|
||||
}
|
||||
module_exit(cyttsp5_spi_exit);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver");
|
||||
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
|
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
* cyttsp5_test_device_access_api.c
|
||||
* Parade TrueTouch(TM) Standard Product V5 Device Access API test module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/uapi/cyttsp5.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define BUFFER_SIZE 256
|
||||
|
||||
#define COMMAND_GET_SYSTEM_INFO 2
|
||||
#define COMMAND_SUSPEND_SCANNING 3
|
||||
#define COMMAND_RESUME_SCANNING 4
|
||||
#define COMMAND_GET_PARAMETER 5
|
||||
#define COMMAND_SET_PARAMETER 6
|
||||
|
||||
#define PARAMETER_ACTIVE_DISTANCE_2 0x0B
|
||||
|
||||
struct tt_output_report {
|
||||
__le16 reg_address;
|
||||
__le16 length;
|
||||
u8 report_id;
|
||||
u8 reserved;
|
||||
u8 command;
|
||||
u8 parameters[0];
|
||||
} __packed;
|
||||
|
||||
struct tt_input_report {
|
||||
__le16 length;
|
||||
u8 report_id;
|
||||
u8 reserved;
|
||||
u8 command;
|
||||
u8 return_data[0];
|
||||
} __packed;
|
||||
|
||||
static int prepare_tt_output_report(struct tt_output_report *out,
|
||||
u16 length, u8 command)
|
||||
{
|
||||
put_unaligned_le16(0x04, &out->reg_address);
|
||||
put_unaligned_le16(5 + length, &out->length);
|
||||
|
||||
out->report_id = 0x2f;
|
||||
out->reserved = 0x00;
|
||||
out->command = command;
|
||||
|
||||
return 7 + length;
|
||||
}
|
||||
|
||||
static int check_and_parse_tt_input_report(struct tt_input_report *in,
|
||||
u16 *length, u8 *command)
|
||||
{
|
||||
if (in->report_id != 0x1f)
|
||||
return -EINVAL;
|
||||
|
||||
*length = get_unaligned_le16(&in->length);
|
||||
*command = in->command & 0x7f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_get_system_info_report(u8 *buf)
|
||||
{
|
||||
struct tt_output_report *out = (struct tt_output_report *)buf;
|
||||
|
||||
return prepare_tt_output_report(out, 0, COMMAND_GET_SYSTEM_INFO);
|
||||
}
|
||||
|
||||
static int check_get_system_info_response(u8 *buf, u16 read_length)
|
||||
{
|
||||
struct tt_input_report *in = (struct tt_input_report *)buf;
|
||||
u16 length = 0;
|
||||
u8 command = 0;
|
||||
|
||||
if (read_length != 51)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_and_parse_tt_input_report(in, &length, &command)
|
||||
|| command != COMMAND_GET_SYSTEM_INFO
|
||||
|| length != 51)
|
||||
return -EINVAL;
|
||||
|
||||
pr_info("PIP Major Version: %d\n", in->return_data[0]);
|
||||
pr_info("PIP Minor Version: %d\n", in->return_data[1]);
|
||||
pr_info("Touch Firmware Product Id: %d\n",
|
||||
get_unaligned_le16(&in->return_data[2]));
|
||||
pr_info("Touch Firmware Major Version: %d\n", in->return_data[4]);
|
||||
pr_info("Touch Firmware Minor Version: %d\n", in->return_data[5]);
|
||||
pr_info("Touch Firmware Internal Revision Control Number: %d\n",
|
||||
get_unaligned_le32(&in->return_data[6]));
|
||||
pr_info("Customer Specified Firmware/Configuration Version: %d\n",
|
||||
get_unaligned_le16(&in->return_data[10]));
|
||||
pr_info("Bootloader Major Version: %d\n", in->return_data[12]);
|
||||
pr_info("Bootloader Minor Version: %d\n", in->return_data[13]);
|
||||
pr_info("Family ID: 0x%02x\n", in->return_data[14]);
|
||||
pr_info("Revision ID: 0x%02x\n", in->return_data[15]);
|
||||
pr_info("Silicon ID: 0x%02x\n",
|
||||
get_unaligned_le16(&in->return_data[16]));
|
||||
pr_info("Parade Manufacturing ID[0]: 0x%02x\n", in->return_data[18]);
|
||||
pr_info("Parade Manufacturing ID[1]: 0x%02x\n", in->return_data[19]);
|
||||
pr_info("Parade Manufacturing ID[2]: 0x%02x\n", in->return_data[20]);
|
||||
pr_info("Parade Manufacturing ID[3]: 0x%02x\n", in->return_data[21]);
|
||||
pr_info("Parade Manufacturing ID[4]: 0x%02x\n", in->return_data[22]);
|
||||
pr_info("Parade Manufacturing ID[5]: 0x%02x\n", in->return_data[23]);
|
||||
pr_info("Parade Manufacturing ID[6]: 0x%02x\n", in->return_data[24]);
|
||||
pr_info("Parade Manufacturing ID[7]: 0x%02x\n", in->return_data[25]);
|
||||
pr_info("POST Result Code: 0x%02x\n",
|
||||
get_unaligned_le16(&in->return_data[26]));
|
||||
|
||||
pr_info("Number of X Electrodes: %d\n", in->return_data[28]);
|
||||
pr_info("Number of Y Electrodes: %d\n", in->return_data[29]);
|
||||
pr_info("Panel X Axis Length: %d\n",
|
||||
get_unaligned_le16(&in->return_data[30]));
|
||||
pr_info("Panel Y Axis Length: %d\n",
|
||||
get_unaligned_le16(&in->return_data[32]));
|
||||
pr_info("Panel X Axis Resolution: %d\n",
|
||||
get_unaligned_le16(&in->return_data[34]));
|
||||
pr_info("Panel Y Axis Resolution: %d\n",
|
||||
get_unaligned_le16(&in->return_data[36]));
|
||||
pr_info("Panel Pressure Resolution: %d\n",
|
||||
get_unaligned_le16(&in->return_data[38]));
|
||||
pr_info("X_ORG: %d\n", in->return_data[40]);
|
||||
pr_info("Y_ORG: %d\n", in->return_data[41]);
|
||||
pr_info("Panel ID: %d\n", in->return_data[42]);
|
||||
pr_info("Buttons: 0x%02x\n", in->return_data[43]);
|
||||
pr_info("BAL SELF MC: 0x%02x\n", in->return_data[44]);
|
||||
pr_info("Max Number of Touch Records per Refresh Cycle: %d\n",
|
||||
in->return_data[45]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_get_parameter_report(u8 *buf, u8 parameter_id)
|
||||
{
|
||||
struct tt_output_report *out = (struct tt_output_report *)buf;
|
||||
|
||||
out->parameters[0] = parameter_id;
|
||||
|
||||
return prepare_tt_output_report(out, 1, COMMAND_GET_PARAMETER);
|
||||
}
|
||||
|
||||
static int check_get_parameter_response(u8 *buf, u16 read_length,
|
||||
u32 *parameter_value)
|
||||
{
|
||||
struct tt_input_report *in = (struct tt_input_report *)buf;
|
||||
u16 length = 0;
|
||||
u8 command = 0;
|
||||
u32 param_value = 0;
|
||||
u8 param_id;
|
||||
u8 param_size = 0;
|
||||
|
||||
if (read_length != 8 && read_length != 9 && read_length != 11)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_and_parse_tt_input_report(in, &length, &command)
|
||||
|| command != COMMAND_GET_PARAMETER
|
||||
|| (length != 8 && length != 9 && length != 11))
|
||||
return -EINVAL;
|
||||
|
||||
param_id = in->return_data[0];
|
||||
|
||||
param_size = in->return_data[1];
|
||||
|
||||
if (param_size == 1)
|
||||
param_value = in->return_data[2];
|
||||
else if (param_size == 2)
|
||||
param_value = get_unaligned_le16(&in->return_data[2]);
|
||||
else if (param_size == 4)
|
||||
param_value = get_unaligned_le32(&in->return_data[2]);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
pr_info("%s: Parameter ID: 0x%02x Value: 0x%02x\n",
|
||||
__func__, param_id, param_value);
|
||||
|
||||
if (parameter_value)
|
||||
*parameter_value = param_value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_set_parameter_report(u8 *buf, u8 parameter_id,
|
||||
u8 parameter_size, u32 parameter_value)
|
||||
{
|
||||
struct tt_output_report *out = (struct tt_output_report *)buf;
|
||||
|
||||
out->parameters[0] = parameter_id;
|
||||
out->parameters[1] = parameter_size;
|
||||
|
||||
if (parameter_size == 1)
|
||||
out->parameters[2] = (u8)parameter_value;
|
||||
else if (parameter_size == 2)
|
||||
put_unaligned_le16(parameter_value, &out->parameters[2]);
|
||||
else if (parameter_size == 4)
|
||||
put_unaligned_le32(parameter_value, &out->parameters[2]);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return prepare_tt_output_report(out, 2 + parameter_size,
|
||||
COMMAND_SET_PARAMETER);
|
||||
}
|
||||
|
||||
static int check_set_parameter_response(u8 *buf, u16 read_length)
|
||||
{
|
||||
struct tt_input_report *in = (struct tt_input_report *)buf;
|
||||
u16 length = 0;
|
||||
u8 command = 0;
|
||||
u8 param_id;
|
||||
u8 param_size = 0;
|
||||
|
||||
if (read_length != 7)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_and_parse_tt_input_report(in, &length, &command)
|
||||
|| command != COMMAND_SET_PARAMETER
|
||||
|| length != 7)
|
||||
return -EINVAL;
|
||||
|
||||
param_id = in->return_data[0];
|
||||
param_size = in->return_data[1];
|
||||
|
||||
pr_info("%s: Parameter ID: 0x%02x Size: 0x%02x\n",
|
||||
__func__, param_id, param_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_suspend_scanning_report(u8 *buf)
|
||||
{
|
||||
struct tt_output_report *out = (struct tt_output_report *)buf;
|
||||
|
||||
return prepare_tt_output_report(out, 0, COMMAND_SUSPEND_SCANNING);
|
||||
}
|
||||
|
||||
static int check_suspend_scanning_response(u8 *buf, u16 read_length)
|
||||
{
|
||||
struct tt_input_report *in = (struct tt_input_report *)buf;
|
||||
u16 length = 0;
|
||||
u8 command = 0;
|
||||
|
||||
if (read_length != 5)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_and_parse_tt_input_report(in, &length, &command)
|
||||
|| command != COMMAND_SUSPEND_SCANNING
|
||||
|| length != 5)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_resume_scanning_report(u8 *buf)
|
||||
{
|
||||
struct tt_output_report *out = (struct tt_output_report *)buf;
|
||||
|
||||
return prepare_tt_output_report(out, 0, COMMAND_RESUME_SCANNING);
|
||||
}
|
||||
|
||||
static int check_resume_scanning_response(u8 *buf, u16 read_length)
|
||||
{
|
||||
struct tt_input_report *in = (struct tt_input_report *)buf;
|
||||
u16 length = 0;
|
||||
u8 command = 0;
|
||||
|
||||
if (read_length != 5)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_and_parse_tt_input_report(in, &length, &command)
|
||||
|| command != COMMAND_RESUME_SCANNING
|
||||
|| length != 5)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cyttsp5_user_command_async_cont(const char *core_name,
|
||||
u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
|
||||
u16 actual_read_length, int rc)
|
||||
{
|
||||
if (rc) {
|
||||
pr_err("%s: suspend scan fails\n", __func__);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = check_suspend_scanning_response(read_buf, actual_read_length);
|
||||
if (rc) {
|
||||
pr_err("%s: check suspend scanning response fails\n", __func__);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pr_info("%s: suspend scanning succeeds\n", __func__);
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read and write buffers */
|
||||
static u8 write_buf[BUFFER_SIZE];
|
||||
static u8 read_buf[BUFFER_SIZE];
|
||||
|
||||
static uint active_distance;
|
||||
module_param(active_distance, uint, 0);
|
||||
|
||||
static int __init cyttsp5_test_device_access_api_init(void)
|
||||
{
|
||||
u32 initial_active_distance;
|
||||
u16 actual_read_len;
|
||||
int write_len;
|
||||
int rc;
|
||||
|
||||
pr_info("%s: Enter\n", __func__);
|
||||
|
||||
/* CASE 1: Run get system information */
|
||||
write_len = prepare_get_system_info_report(write_buf);
|
||||
|
||||
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
|
||||
read_buf, write_len, write_buf,
|
||||
&actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = check_get_system_info_response(read_buf, actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
/* CASE 2: Run get parameter (Active distance) */
|
||||
write_len = prepare_get_parameter_report(write_buf,
|
||||
PARAMETER_ACTIVE_DISTANCE_2);
|
||||
|
||||
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
|
||||
read_buf, write_len, write_buf,
|
||||
&actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = check_get_parameter_response(read_buf, actual_read_len,
|
||||
&initial_active_distance);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
pr_info("%s: Initial Active Distance: %d\n",
|
||||
__func__, initial_active_distance);
|
||||
|
||||
/* CASE 3: Run set parameter (Active distance) */
|
||||
write_len = prepare_set_parameter_report(write_buf,
|
||||
PARAMETER_ACTIVE_DISTANCE_2, 1,
|
||||
active_distance);
|
||||
|
||||
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
|
||||
read_buf, write_len, write_buf,
|
||||
&actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = check_set_parameter_response(read_buf, actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
pr_info("%s: Active Distance set to %d\n", __func__, active_distance);
|
||||
|
||||
/* CASE 4: Run get parameter (Active distance) */
|
||||
write_len = prepare_get_parameter_report(write_buf,
|
||||
PARAMETER_ACTIVE_DISTANCE_2);
|
||||
|
||||
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
|
||||
read_buf, write_len, write_buf,
|
||||
&actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = check_get_parameter_response(read_buf, actual_read_len,
|
||||
&active_distance);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
pr_info("%s: New Active Distance: %d\n", __func__, active_distance);
|
||||
|
||||
/* CASE 5: Run suspend scanning asynchronously */
|
||||
write_len = prepare_suspend_scanning_report(write_buf);
|
||||
|
||||
preempt_disable();
|
||||
rc = cyttsp5_device_access_user_command_async(NULL,
|
||||
sizeof(read_buf), read_buf, write_len, write_buf,
|
||||
cyttsp5_user_command_async_cont);
|
||||
preempt_enable();
|
||||
if (rc)
|
||||
goto exit;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
module_init(cyttsp5_test_device_access_api_init);
|
||||
|
||||
static void __exit cyttsp5_test_device_access_api_exit(void)
|
||||
{
|
||||
u16 actual_read_len;
|
||||
int write_len;
|
||||
int rc;
|
||||
|
||||
/* CASE 6: Run resume scanning */
|
||||
write_len = prepare_resume_scanning_report(write_buf);
|
||||
|
||||
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
|
||||
read_buf, write_len, write_buf,
|
||||
&actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = check_resume_scanning_response(read_buf, actual_read_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
pr_info("%s: resume scanning succeeds\n", __func__);
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
module_exit(cyttsp5_test_device_access_api_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver API Tester");
|
||||
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
|
|
@ -13,114 +13,336 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#define WACOM_CMD_QUERY0 0x04
|
||||
#define WACOM_CMD_QUERY1 0x00
|
||||
#define WACOM_CMD_QUERY2 0x33
|
||||
#define WACOM_CMD_QUERY3 0x02
|
||||
#define WACOM_CMD_THROW0 0x05
|
||||
#define WACOM_CMD_THROW1 0x00
|
||||
#define WACOM_QUERY_SIZE 19
|
||||
// Bitmasks (for data[3])
|
||||
#define WACOM_TIP_SWITCH_bm (1 << 0)
|
||||
#define WACOM_BARREL_SWITCH_bm (1 << 1)
|
||||
#define WACOM_ERASER_bm (1 << 2)
|
||||
#define WACOM_INVERT_bm (1 << 3)
|
||||
#define WACOM_BARREL_SWITCH_2_bm (1 << 4)
|
||||
#define WACOM_IN_RANGE_bm (1 << 5)
|
||||
|
||||
// Registers
|
||||
#define WACOM_COMMAND_LSB 0x04
|
||||
#define WACOM_COMMAND_MSB 0x00
|
||||
|
||||
#define WACOM_DATA_LSB 0x05
|
||||
#define WACOM_DATA_MSB 0x00
|
||||
|
||||
// Report types
|
||||
#define REPORT_INPUT 0x10
|
||||
#define REPORT_OUTPUT 0x20
|
||||
#define REPORT_FEATURE 0x30
|
||||
|
||||
// Requests / operations
|
||||
#define OPCODE_RESET 0x01
|
||||
#define OPCODE_GET_REPORT 0x02
|
||||
#define OPCODE_SET_REPORT 0x03
|
||||
#define OPCODE_SET_POWER 0x08
|
||||
|
||||
// Power settings
|
||||
#define POWER_ON 0x00
|
||||
#define POWER_SLEEP 0x01
|
||||
|
||||
// Input report ids
|
||||
#define WACOM_PEN_DATA_REPORT 2
|
||||
#define WACOM_SHINONOME_REPORT 26
|
||||
|
||||
// Feature report ids
|
||||
#define WACOM_DEVICE_MODE_REPORT 2
|
||||
#define WACOM_QUERY_REPORT 3
|
||||
#define WACOM_PEN_INPUT_FORMAT_REPORT 9
|
||||
#define WACOM_POSITION_RATE_REPORT 25
|
||||
#define WACOM_ORIGIN_OFFSET_REPORT 22
|
||||
#define WACOM_SIDE_SWITCH_REPORT 21
|
||||
|
||||
#define WACOM_MAX_DATA_SIZE 22
|
||||
|
||||
struct wacom_features {
|
||||
int x_max;
|
||||
int y_max;
|
||||
int pressure_max;
|
||||
int distance_max;
|
||||
int distance_physical_max;
|
||||
int tilt_x_max;
|
||||
int tilt_y_max;
|
||||
char fw_version;
|
||||
};
|
||||
|
||||
struct wacom_i2c {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
u8 data[WACOM_QUERY_SIZE];
|
||||
struct wacom_features features;
|
||||
struct regulator *vdd;
|
||||
u8 data[WACOM_MAX_DATA_SIZE];
|
||||
bool prox;
|
||||
int tool;
|
||||
|
||||
bool flip_tilt_x;
|
||||
bool flip_tilt_y;
|
||||
bool flip_pos_x;
|
||||
bool flip_pos_y;
|
||||
bool flip_distance;
|
||||
bool flip_pressure;
|
||||
};
|
||||
|
||||
u8 reset_cmd[] = {
|
||||
WACOM_COMMAND_LSB,
|
||||
WACOM_COMMAND_MSB,
|
||||
0x00,
|
||||
OPCODE_RESET,
|
||||
};
|
||||
|
||||
u8 wakeup_cmd[] = {
|
||||
WACOM_COMMAND_LSB,
|
||||
WACOM_COMMAND_MSB,
|
||||
POWER_ON,
|
||||
OPCODE_SET_POWER,
|
||||
};
|
||||
|
||||
u8 sleep_cmd[] = {
|
||||
WACOM_COMMAND_LSB,
|
||||
WACOM_COMMAND_MSB,
|
||||
POWER_SLEEP,
|
||||
OPCODE_SET_POWER,
|
||||
};
|
||||
|
||||
static int wacom_query_device(struct i2c_client *client,
|
||||
struct wacom_features *features)
|
||||
struct wacom_features *features)
|
||||
{
|
||||
int ret;
|
||||
u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1,
|
||||
WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 };
|
||||
u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 };
|
||||
u8 data[WACOM_QUERY_SIZE];
|
||||
u8 data[WACOM_MAX_DATA_SIZE];
|
||||
struct reset_control *rstc;
|
||||
|
||||
u8 get_query_data_cmd[] = {
|
||||
WACOM_COMMAND_LSB,
|
||||
WACOM_COMMAND_MSB,
|
||||
REPORT_FEATURE | WACOM_QUERY_REPORT,
|
||||
OPCODE_GET_REPORT,
|
||||
WACOM_DATA_LSB,
|
||||
WACOM_DATA_MSB,
|
||||
};
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
// Request reading of feature ReportID: 3 (Pen Query Data)
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(cmd1),
|
||||
.buf = cmd1,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(cmd2),
|
||||
.buf = cmd2,
|
||||
.len = sizeof(get_query_data_cmd),
|
||||
.buf = get_query_data_cmd,
|
||||
},
|
||||
// Read 21 bytes
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(data),
|
||||
.len = 21,
|
||||
.buf = data,
|
||||
},
|
||||
};
|
||||
|
||||
rstc = devm_reset_control_get_optional_exclusive(&client->dev, NULL);
|
||||
if (IS_ERR(rstc)) {
|
||||
dev_err(&client->dev, "Failed to get reset control before init\n");
|
||||
}
|
||||
else {
|
||||
reset_control_reset(rstc);
|
||||
}
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
}
|
||||
if (ret != ARRAY_SIZE(msgs)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
features->x_max = get_unaligned_le16(&data[3]);
|
||||
features->y_max = get_unaligned_le16(&data[5]);
|
||||
features->pressure_max = get_unaligned_le16(&data[11]);
|
||||
features->fw_version = get_unaligned_le16(&data[13]);
|
||||
features->distance_max = data[15];
|
||||
features->distance_physical_max = data[16];
|
||||
features->tilt_x_max = get_unaligned_le16(&data[17]);
|
||||
features->tilt_y_max = get_unaligned_le16(&data[19]);
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"x_max:%d, y_max:%d, pressure:%d, fw:%d\n",
|
||||
"x_max:%d, y_max:%d, pressure:%d, fw:%d, "
|
||||
"distance: %d, phys distance: %d"
|
||||
"tilt_x_max: %d, tilt_y_max: %d\n",
|
||||
features->x_max, features->y_max,
|
||||
features->pressure_max, features->fw_version);
|
||||
features->pressure_max, features->fw_version,
|
||||
features->distance_max, features->distance_physical_max,
|
||||
features->tilt_x_max, features->tilt_y_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static void wacom_of_read(struct wacom_i2c *wac_i2c)
|
||||
{
|
||||
struct i2c_client *client = wac_i2c->client;
|
||||
|
||||
wac_i2c->flip_tilt_x = of_property_read_bool(client->dev.of_node, "flip-tilt-x");
|
||||
wac_i2c->flip_tilt_y = of_property_read_bool(client->dev.of_node, "flip-tilt-y");
|
||||
wac_i2c->flip_pos_x = of_property_read_bool(client->dev.of_node, "flip-pos-x");
|
||||
wac_i2c->flip_pos_y = of_property_read_bool(client->dev.of_node, "flip-pos-y");
|
||||
wac_i2c->flip_distance = of_property_read_bool(client->dev.of_node, "flip-distance");
|
||||
wac_i2c->flip_pressure = of_property_read_bool(client->dev.of_node, "flip-pressure");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int wacom_setup_device(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
u8 data[WACOM_MAX_DATA_SIZE];
|
||||
|
||||
u8 get_sample_rate_cmd[] = {
|
||||
WACOM_COMMAND_LSB,
|
||||
WACOM_COMMAND_MSB,
|
||||
REPORT_FEATURE | 0xF,
|
||||
OPCODE_GET_REPORT,
|
||||
WACOM_POSITION_RATE_REPORT,
|
||||
WACOM_DATA_LSB,
|
||||
WACOM_DATA_MSB,
|
||||
};
|
||||
|
||||
u8 set_sample_rate_cmd[] = {
|
||||
WACOM_COMMAND_LSB,
|
||||
WACOM_COMMAND_MSB,
|
||||
REPORT_FEATURE | 0xF,
|
||||
OPCODE_SET_REPORT,
|
||||
WACOM_POSITION_RATE_REPORT,
|
||||
WACOM_DATA_LSB,
|
||||
WACOM_DATA_MSB,
|
||||
0x04, // LENGTH LSB
|
||||
0x00, // LENGTH MSB
|
||||
WACOM_POSITION_RATE_REPORT,
|
||||
0x06, // Selected rate (1=240pps, 3=360pps (def), 5=480pps, 6=540pps)
|
||||
};
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
// Request writing of feature, ReportID 25 / Position Report Rate
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(set_sample_rate_cmd),
|
||||
.buf = set_sample_rate_cmd,
|
||||
},
|
||||
};
|
||||
|
||||
struct i2c_msg msgs_readback[] = {
|
||||
// Request reading of feature, ReportID 25 / Position Report Rate
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(get_sample_rate_cmd),
|
||||
.buf = get_sample_rate_cmd,
|
||||
},
|
||||
// Read 4 bytes
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 4,
|
||||
.buf = data,
|
||||
},
|
||||
};
|
||||
|
||||
// Write position rate report
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != ARRAY_SIZE(msgs)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Read position rate report
|
||||
ret = i2c_transfer(client->adapter, msgs_readback, ARRAY_SIZE(msgs_readback));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != ARRAY_SIZE(msgs_readback)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t wacom_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
ktime_t timestamp = ktime_get();
|
||||
|
||||
struct wacom_i2c *wac_i2c = dev_id;
|
||||
struct input_dev *input = wac_i2c->input;
|
||||
struct wacom_features *features = &wac_i2c->features;
|
||||
u8 *data = wac_i2c->data;
|
||||
unsigned int x, y, pressure;
|
||||
unsigned char tsw, f1, f2, ers;
|
||||
unsigned char tip, f1, f2, eraser, transducer = 0;
|
||||
short tilt_x, tilt_y, distance;
|
||||
int error;
|
||||
|
||||
error = i2c_master_recv(wac_i2c->client,
|
||||
wac_i2c->data, sizeof(wac_i2c->data));
|
||||
if (error < 0)
|
||||
wac_i2c->data, 17);
|
||||
if (error < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
tsw = data[3] & 0x01;
|
||||
ers = data[3] & 0x04;
|
||||
f1 = data[3] & 0x02;
|
||||
f2 = data[3] & 0x10;
|
||||
// data[0] == Length LSB
|
||||
// data[1] == Length MSB
|
||||
// data[2] == ReportID (2 | 26)
|
||||
tip = data[3] & WACOM_TIP_SWITCH_bm;
|
||||
eraser = data[3] & WACOM_ERASER_bm;
|
||||
f1 = data[3] & WACOM_BARREL_SWITCH_bm;
|
||||
f2 = data[3] & WACOM_BARREL_SWITCH_2_bm;
|
||||
x = le16_to_cpup((__le16 *)&data[4]);
|
||||
y = le16_to_cpup((__le16 *)&data[6]);
|
||||
pressure = le16_to_cpup((__le16 *)&data[8]);
|
||||
|
||||
// Shinonome Refill has a transducer index field:
|
||||
if (data[2] == WACOM_SHINONOME_REPORT) {
|
||||
transducer = (data[3] >> 6);
|
||||
}
|
||||
|
||||
// Tilt (signed)
|
||||
tilt_x = le16_to_cpup((__le16 *)&data[11]);
|
||||
tilt_y = le16_to_cpup((__le16 *)&data[13]);
|
||||
|
||||
// Hover height
|
||||
distance = le16_to_cpup((__le16 *)&data[15]);
|
||||
|
||||
if (!wac_i2c->prox)
|
||||
wac_i2c->tool = (data[3] & 0x0c) ?
|
||||
BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
|
||||
wac_i2c->prox = data[3] & 0x20;
|
||||
wac_i2c->prox = (data[3] & 0x20) ? true : false;
|
||||
|
||||
input_report_key(input, BTN_TOUCH, tsw || ers);
|
||||
// Flippin'
|
||||
pressure = wac_i2c->flip_pressure ? (features->pressure_max - pressure) : pressure;
|
||||
distance = wac_i2c->flip_distance ? -distance : distance;
|
||||
x = wac_i2c->flip_pos_x ? (features->x_max - x) : x;
|
||||
y = wac_i2c->flip_pos_y ? (features->y_max - y) : y;
|
||||
tilt_x = wac_i2c->flip_tilt_x ? -tilt_x : tilt_x;
|
||||
tilt_y = wac_i2c->flip_tilt_y ? -tilt_y : tilt_y;
|
||||
|
||||
input_report_key(input, BTN_TOUCH, tip || eraser);
|
||||
input_report_key(input, wac_i2c->tool, wac_i2c->prox);
|
||||
input_report_key(input, BTN_STYLUS, f1);
|
||||
input_report_key(input, BTN_STYLUS2, f2);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, pressure);
|
||||
input_report_abs(input, ABS_DISTANCE, distance);
|
||||
input_report_abs(input, ABS_TILT_X, tilt_x);
|
||||
input_report_abs(input, ABS_TILT_Y, tilt_y);
|
||||
input_set_timestamp(input, timestamp);
|
||||
input_sync(input);
|
||||
|
||||
out:
|
||||
|
@ -146,36 +368,55 @@ static void wacom_i2c_close(struct input_dev *dev)
|
|||
}
|
||||
|
||||
static int wacom_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct wacom_i2c *wac_i2c;
|
||||
struct wacom_features *features;
|
||||
struct input_dev *input;
|
||||
struct wacom_features features = { 0 };
|
||||
int error;
|
||||
|
||||
wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
|
||||
if (!wac_i2c) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
features = &wac_i2c->features;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "i2c_check_functionality error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error = wacom_query_device(client, &features);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!wac_i2c || !input) {
|
||||
error = -ENOMEM;
|
||||
wac_i2c->vdd = regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(wac_i2c->vdd)) {
|
||||
error = PTR_ERR(wac_i2c->vdd);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = regulator_enable(wac_i2c->vdd);
|
||||
if (error)
|
||||
goto err_put_vdd;
|
||||
|
||||
error = wacom_query_device(client, features);
|
||||
if (error)
|
||||
goto err_disable_vdd;
|
||||
|
||||
error = wacom_setup_device(client);
|
||||
if (error)
|
||||
goto err_disable_vdd;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
error = -ENOMEM;
|
||||
goto err_disable_vdd;
|
||||
}
|
||||
|
||||
wac_i2c->client = client;
|
||||
wac_i2c->input = input;
|
||||
|
||||
input->name = "Wacom I2C Digitizer";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->id.vendor = 0x56a;
|
||||
input->id.version = features.fw_version;
|
||||
input->id.version = features->fw_version;
|
||||
input->dev.parent = &client->dev;
|
||||
input->open = wacom_i2c_open;
|
||||
input->close = wacom_i2c_close;
|
||||
|
@ -188,20 +429,24 @@ static int wacom_i2c_probe(struct i2c_client *client,
|
|||
__set_bit(BTN_STYLUS2, input->keybit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
|
||||
input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_X, 0, features->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, features->y_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE,
|
||||
0, features.pressure_max, 0, 0);
|
||||
0, features->pressure_max, 0, 0);
|
||||
|
||||
input_set_abs_params(input, ABS_DISTANCE, 0, features->distance_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_TILT_X, -features->tilt_x_max, features->tilt_x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_TILT_Y, -features->tilt_y_max, features->tilt_y_max, 0, 0);
|
||||
|
||||
input_set_drvdata(input, wac_i2c);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"wacom_i2c", wac_i2c);
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"wacom_i2c", wac_i2c);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable IRQ, error: %d\n", error);
|
||||
goto err_free_mem;
|
||||
goto err_free_moremem;
|
||||
}
|
||||
|
||||
/* Disable the IRQ, we'll enable it in wac_i2c_open() */
|
||||
|
@ -215,12 +460,24 @@ static int wacom_i2c_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
i2c_set_clientdata(client, wac_i2c);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
wacom_of_read(wac_i2c);
|
||||
#endif
|
||||
|
||||
device_init_wakeup(&client->dev, true);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, wac_i2c);
|
||||
err_free_mem:
|
||||
err_free_moremem:
|
||||
input_free_device(input);
|
||||
err_disable_vdd:
|
||||
regulator_disable(wac_i2c->vdd);
|
||||
err_put_vdd:
|
||||
regulator_put(wac_i2c->vdd);
|
||||
err_free_mem:
|
||||
kfree(wac_i2c);
|
||||
|
||||
return error;
|
||||
|
@ -232,6 +489,8 @@ static int wacom_i2c_remove(struct i2c_client *client)
|
|||
|
||||
free_irq(client->irq, wac_i2c);
|
||||
input_unregister_device(wac_i2c->input);
|
||||
regulator_disable(wac_i2c->vdd);
|
||||
regulator_put(wac_i2c->vdd);
|
||||
kfree(wac_i2c);
|
||||
|
||||
return 0;
|
||||
|
@ -240,7 +499,15 @@ static int wacom_i2c_remove(struct i2c_client *client)
|
|||
static int __maybe_unused wacom_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
|
||||
|
||||
if (pm_suspend_target_state == PM_SUSPEND_MEM) {
|
||||
regulator_disable(wac_i2c->vdd);
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(client->irq);
|
||||
disable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
|
@ -249,8 +516,24 @@ static int __maybe_unused wacom_i2c_suspend(struct device *dev)
|
|||
static int __maybe_unused wacom_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
enable_irq(client->irq);
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
if (pm_suspend_target_state == PM_SUSPEND_MEM) {
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = regulator_enable(wac_i2c->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = wacom_setup_device(client);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -263,10 +546,21 @@ static const struct i2c_device_id wacom_i2c_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wacom_i2c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id wacom_i2c_of_match_table[] = {
|
||||
{ .compatible = "wacom,wacom-i2c" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wacom_i2c_of_match_table);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver wacom_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wacom_i2c",
|
||||
.pm = &wacom_i2c_pm,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = of_match_ptr(wacom_i2c_of_match_table),
|
||||
#endif
|
||||
},
|
||||
|
||||
.probe = wacom_i2c_probe,
|
||||
|
|
|
@ -878,6 +878,17 @@ config MFD_MAX8998
|
|||
additional drivers must be enabled in order to use the functionality
|
||||
of the device.
|
||||
|
||||
config MFD_MAX77818
|
||||
bool "Maxim Semiconductor MAX77818 REGULATOR/CHARGER/FUEL-GAUGE Support"
|
||||
depends on I2C=y
|
||||
select BATTERY_MAX77818_UTILS
|
||||
select MFD_CORE
|
||||
select IRW_DOMAIN
|
||||
help
|
||||
Say yes here to add support for Maxime Semiconductor MAX77818 device.
|
||||
The device has a regulator part, a charger part and a fuel-gauge part
|
||||
which may be enabled as required (power management devices).
|
||||
|
||||
config MFD_MT6397
|
||||
tristate "MediaTek MT6397 PMIC Support"
|
||||
select MFD_CORE
|
||||
|
@ -1388,6 +1399,16 @@ config MFD_PALMAS
|
|||
If you say yes here you get support for the Palmas
|
||||
series of PMIC chips from Texas Instruments.
|
||||
|
||||
config MFD_SY7636A
|
||||
tristate "Silergy SY7636A Power Management chip driver"
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
depends on I2C=y
|
||||
help
|
||||
If you say yes here you get support for the SY7636A
|
||||
PMIC chip from Silergy
|
||||
|
||||
config TPS6105X
|
||||
tristate "TI TPS61050/61052 Boost Converters"
|
||||
depends on I2C
|
||||
|
@ -1997,6 +2018,14 @@ config MFD_PCA9450
|
|||
if you say yes here you get support for the PCA9450
|
||||
Power Management chips.
|
||||
|
||||
config MFD_BD7181X
|
||||
bool "BD71815/BD71817 Power Management chip"
|
||||
depends on I2C=y
|
||||
select MFD_CORE
|
||||
help
|
||||
if you say yes here you get support for the BD71815/BD71817
|
||||
Power Management chips.
|
||||
|
||||
menu "Multimedia Capabilities Port drivers"
|
||||
depends on ARCH_SA1100
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@ max8925-objs := max8925-core.o max8925-i2c.o
|
|||
obj-$(CONFIG_MFD_MAX8925) += max8925.o
|
||||
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
|
||||
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
|
||||
obj-$(CONFIG_MFD_MAX77818) += max77818.o
|
||||
|
||||
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
|
||||
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
|
||||
|
@ -235,6 +236,8 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
|
|||
obj-$(CONFIG_MFD_DLN2) += dln2.o
|
||||
obj-$(CONFIG_MFD_RT5033) += rt5033.o
|
||||
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
|
||||
obj-$(CONFIG_MFD_SY7636A) += sy7636a.o
|
||||
obj-$(CONFIG_MFD_MXC_HDMI) += mxc-hdmi-core.o
|
||||
|
||||
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
|
||||
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
|
||||
|
@ -260,3 +263,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
|
|||
obj-$(CONFIG_MFD_STMFX) += stmfx.o
|
||||
obj-$(CONFIG_MFD_PCA9450) += pca9450.o
|
||||
|
||||
obj-$(CONFIG_MFD_BD7181X) += bd7181x.o
|
||||
obj-$(CONFIG_MFD_BD71837) += bd71837.o
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* @file bd7181x.c -- RoHM BD7181X/BD71817 mfd driver
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author: Tony Luo <luofc@embedinfo.com>
|
||||
* Copyright 2014 Embest Technology Co. Ltd. Inc.
|
||||
*/
|
||||
#define DEBUG
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/bd7181x.h>
|
||||
|
||||
/** @brief bd7181x irq resource */
|
||||
static struct resource rtc_resources[] = {
|
||||
{
|
||||
.start = BD7181X_IRQ_ALARM_12,
|
||||
.end = BD7181X_IRQ_ALARM_12,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct resource power_resources[] = {
|
||||
// irq# 0
|
||||
{
|
||||
.start = BD7181X_IRQ_DCIN_03,
|
||||
.end = BD7181X_IRQ_DCIN_03,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
// irq# 1
|
||||
{
|
||||
.start = BD7181X_IRQ_BAT_MON_08,
|
||||
.end = BD7181X_IRQ_BAT_MON_08,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
// irq# 2
|
||||
{
|
||||
.start = BD7181X_IRQ_TEMPERATURE_11,
|
||||
.end = BD7181X_IRQ_TEMPERATURE_11,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief bd7181x multi function cells */
|
||||
static struct mfd_cell bd7181x_mfd_cells[] = {
|
||||
{
|
||||
.name = "bd7181x-pmic",
|
||||
},
|
||||
{
|
||||
.name = "bd7181x-power",
|
||||
.num_resources = ARRAY_SIZE(power_resources),
|
||||
.resources = &power_resources[0],
|
||||
},
|
||||
{
|
||||
.name = "bd7181x-gpo",
|
||||
},
|
||||
{
|
||||
.name = "bd7181x-rtc",
|
||||
.num_resources = ARRAY_SIZE(rtc_resources),
|
||||
.resources = &rtc_resources[0],
|
||||
},
|
||||
};
|
||||
|
||||
/** @brief bd7181x irqs */
|
||||
static const struct regmap_irq bd7181x_irqs[] = {
|
||||
[BD7181X_IRQ_BUCK_01] = {
|
||||
.mask = BD7181X_INT_EN_01_BUCKAST_MASK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[BD7181X_IRQ_DCIN_02] = {
|
||||
.mask = BD7181X_INT_EN_02_DCINAST_MASK,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[BD7181X_IRQ_DCIN_03] = {
|
||||
.mask = BD7181X_INT_EN_03_DCINAST_MASK,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[BD7181X_IRQ_VSYS_04] = {
|
||||
.mask = BD7181X_INT_EN_04_VSYSAST_MASK,
|
||||
.reg_offset = 4,
|
||||
},
|
||||
[BD7181X_IRQ_CHARGE_05] = {
|
||||
.mask = BD7181X_INT_EN_05_CHGAST_MASK,
|
||||
.reg_offset = 5,
|
||||
},
|
||||
[BD7181X_IRQ_BAT_06] = {
|
||||
.mask = BD7181X_INT_EN_06_BATAST_MASK,
|
||||
.reg_offset = 6,
|
||||
},
|
||||
[BD7181X_IRQ_BAT_MON_07] = {
|
||||
.mask = BD7181X_INT_EN_07_BMONAST_MASK,
|
||||
.reg_offset = 7,
|
||||
},
|
||||
[BD7181X_IRQ_BAT_MON_08] = {
|
||||
.mask = BD7181X_INT_EN_08_BMONAST_MASK,
|
||||
.reg_offset = 8,
|
||||
},
|
||||
[BD7181X_IRQ_BAT_MON_09] = {
|
||||
.mask = BD7181X_INT_EN_09_BMONAST_MASK,
|
||||
.reg_offset = 9,
|
||||
},
|
||||
[BD7181X_IRQ_BAT_MON_10] = {
|
||||
.mask = BD7181X_INT_EN_10_BMONAST_MASK,
|
||||
.reg_offset = 10,
|
||||
},
|
||||
[BD7181X_IRQ_TEMPERATURE_11] = {
|
||||
.mask = BD7181X_INT_EN_11_TMPAST_MASK,
|
||||
.reg_offset = 11,
|
||||
},
|
||||
[BD7181X_IRQ_ALARM_12] = {
|
||||
.mask = BD7181X_INT_EN_12_ALMAST_MASK,
|
||||
.reg_offset = 12,
|
||||
},
|
||||
};
|
||||
|
||||
/** @brief bd7181x irq chip definition */
|
||||
static struct regmap_irq_chip bd7181x_irq_chip = {
|
||||
.name = "bd7181x",
|
||||
.irqs = bd7181x_irqs,
|
||||
.num_irqs = ARRAY_SIZE(bd7181x_irqs),
|
||||
.num_regs = 13,
|
||||
.irq_reg_stride = 1,
|
||||
.status_base = BD7181X_REG_INT_STAT,
|
||||
.mask_base = BD7181X_REG_INT_EN_01 - 1,
|
||||
.mask_invert = true,
|
||||
// .ack_base = BD7181X_REG_INT_STAT_00,
|
||||
};
|
||||
|
||||
/** @brief bd7181x irq initialize
|
||||
* @param bd7181x bd7181x device to init
|
||||
* @param bdinfo platform init data
|
||||
* @retval 0 probe success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_irq_init(struct bd7181x *bd7181x, struct bd7181x_board* bdinfo) {
|
||||
int irq;
|
||||
int ret = 0;
|
||||
|
||||
if (!bdinfo) {
|
||||
dev_warn(bd7181x->dev, "No interrupt support, no pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = gpio_to_irq(bdinfo->gpio_intr);
|
||||
|
||||
bd7181x->chip_irq = irq;
|
||||
printk("bd7181x->chip_irq=%d \n", bd7181x->chip_irq);
|
||||
ret = regmap_add_irq_chip(bd7181x->regmap, bd7181x->chip_irq,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_FALLING, bdinfo->irq_base,
|
||||
&bd7181x_irq_chip, &bd7181x->irq_data);
|
||||
if (ret < 0) {
|
||||
dev_warn(bd7181x->dev, "Failed to add irq_chip %d\n", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @brief bd7181x irq initialize
|
||||
* @param bd7181x bd7181x device to init
|
||||
* @retval 0 probe success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_irq_exit(struct bd7181x *bd7181x)
|
||||
{
|
||||
if (bd7181x->chip_irq > 0)
|
||||
regmap_del_irq_chip(bd7181x->chip_irq, bd7181x->irq_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief check whether volatile register
|
||||
* @param dev kernel device pointer
|
||||
* @param reg register index
|
||||
*/
|
||||
static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
// struct bd7181x *bd7181x = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* Caching all regulator registers.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief regmap configures */
|
||||
static const struct regmap_config bd7181x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = is_volatile_reg,
|
||||
.max_register = BD7181X_MAX_REGISTER - 1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id bd7181x_of_match[] = {
|
||||
{ .compatible = "rohm,bd71815", .data = (void *)0},
|
||||
{ .compatible = "rohm,bd71817", .data = (void *)1},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bd7181x_of_match);
|
||||
|
||||
|
||||
/** @brief parse device tree data of bd7181x
|
||||
* @param client client object provided by system
|
||||
* @param chip_id return chip id back to caller
|
||||
* @return board initialize data
|
||||
*/
|
||||
static struct bd7181x_board *bd7181x_parse_dt(struct i2c_client *client,
|
||||
int *chip_id)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct bd7181x_board *board_info;
|
||||
unsigned int prop;
|
||||
const struct of_device_id *match;
|
||||
int r = 0;
|
||||
|
||||
match = of_match_device(bd7181x_of_match, &client->dev);
|
||||
if (!match) {
|
||||
dev_err(&client->dev, "Failed to find matching dt id\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*chip_id = (int)match->data;
|
||||
|
||||
board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
|
||||
GFP_KERNEL);
|
||||
if (!board_info) {
|
||||
dev_err(&client->dev, "Failed to allocate pdata\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
board_info->gpio_intr = of_get_named_gpio(np, "gpio_intr", 0);
|
||||
if (!gpio_is_valid(board_info->gpio_intr)) {
|
||||
dev_err(&client->dev, "no pmic intr pin available\n");
|
||||
goto err_intr;
|
||||
}
|
||||
|
||||
r = of_property_read_u32(np, "irq_base", &prop);
|
||||
if (!r) {
|
||||
board_info->irq_base = prop;
|
||||
} else {
|
||||
board_info->irq_base = -1;
|
||||
}
|
||||
|
||||
return board_info;
|
||||
|
||||
err_intr:
|
||||
devm_kfree(&client->dev, board_info);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static inline
|
||||
struct bd7181x_board *bd7181x_parse_dt(struct i2c_client *client,
|
||||
int *chip_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bd7181x_hw_init(struct bd7181x *bd7181x)
|
||||
{
|
||||
/* Clear LPSR_MODE bit to get into SNVS state when PWRON goes low */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_PWRCTRL, BIT(4));
|
||||
|
||||
/* Change the default to turn off LDO3 in SNVS state */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE2, BIT(7));
|
||||
|
||||
/* Turn off BUCK1 (VDD_ARM_1V1) in LPSR and SUSPEND mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_BUCK1_MODE, BIT(1) | BIT(0));
|
||||
|
||||
/* Turn off BUCK2 (VDD_SOC_1V0) in LPSR mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_BUCK2_MODE, BIT(1));
|
||||
|
||||
/* Keep BUCK3 (VDD_1V8) on in LPSR mode, as it supplies DDR VDD1 */
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_BUCK3_MODE, BIT(1));
|
||||
|
||||
/* Keep BUCK4 (VDD_DRAM_1V2) on in LPSR mode for DDR retention */
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_BUCK4_MODE, BIT(1));
|
||||
|
||||
/* Turn off BUCK5 (VDD_3V3) in LPSR mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_BUCK5_MODE, BIT(1));
|
||||
|
||||
/* Turn off LDO1_3V3 (NVCC_GPIO2) in LPSR mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE1, BIT(5));
|
||||
|
||||
/*
|
||||
* Keep LDO2_3V3 (NVCC_GPIO1) on in LPSR mode, as we have wakeup
|
||||
* source from there.
|
||||
*/
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_LDO_MODE2, BIT(1));
|
||||
|
||||
/* Turn off LDO3 (VDDA_USB_3P3) in SNVS, LPSR and SUSPEND mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE2, BIT(7) | BIT(5) | BIT(4));
|
||||
|
||||
/* Turn off LDO4_3V3 (EPD screen) in LPSR and SUSPEND mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE3, BIT(1) | BIT(0));
|
||||
|
||||
/* Turn off LDO5_1V8 (SD/MMC) in LPSR mode */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE3, BIT(5));
|
||||
|
||||
/* Keep LDO_DVREF (LPDDR_VREF_0V6) on in LPSR mode */
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_LDO_MODE4, BIT(5));
|
||||
|
||||
/*
|
||||
* Misc settings for a bit more power saving by disabling
|
||||
* unneeded stuff.
|
||||
*
|
||||
* Set LDO4_REG_MODE bit to get LDO4 controlled by register than
|
||||
* external pin (LDO4VEN).
|
||||
*/
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_LDO_MODE1, BIT(3));
|
||||
|
||||
/* Enable OUT32K and have it in cmos mode */
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_OUT32K, BIT(1) | BIT(0));
|
||||
|
||||
/* Disable all charger stuff */
|
||||
bd7181x_reg_write(bd7181x, BD7181X_REG_CHG_SET1, 0x0);
|
||||
|
||||
/* Disable charger thermal shutdown and battery detection */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_CHG_SET2, BIT(7) | BIT(4));
|
||||
|
||||
/* Disable LED lighting */
|
||||
bd7181x_reg_write(bd7181x, BD7181X_REG_CHG_LED_1, 0x17);
|
||||
|
||||
/* Disable coulomb counter */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_CC_CTRL, BIT(6));
|
||||
|
||||
/* Disable Relax State detection */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_REX_CTRL_1, BIT(3));
|
||||
}
|
||||
|
||||
/** @brief probe bd7181x device
|
||||
* @param i2c client object provided by system
|
||||
* @param id chip id
|
||||
* @retval 0 probe success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct bd7181x *bd7181x;
|
||||
struct bd7181x_board *pmic_plat_data;
|
||||
struct bd7181x_board *of_pmic_plat_data = NULL;
|
||||
int chip_id = id->driver_data;
|
||||
int ret = 0;
|
||||
|
||||
pmic_plat_data = dev_get_platdata(&i2c->dev);
|
||||
|
||||
if (!pmic_plat_data && i2c->dev.of_node) {
|
||||
pmic_plat_data = bd7181x_parse_dt(i2c, &chip_id);
|
||||
of_pmic_plat_data = pmic_plat_data;
|
||||
}
|
||||
|
||||
if (!pmic_plat_data)
|
||||
return -EINVAL;
|
||||
|
||||
bd7181x = kzalloc(sizeof(struct bd7181x), GFP_KERNEL);
|
||||
if (bd7181x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
bd7181x->of_plat_data = of_pmic_plat_data;
|
||||
i2c_set_clientdata(i2c, bd7181x);
|
||||
bd7181x->dev = &i2c->dev;
|
||||
bd7181x->i2c_client = i2c;
|
||||
bd7181x->id = chip_id;
|
||||
mutex_init(&bd7181x->io_mutex);
|
||||
|
||||
bd7181x->regmap = devm_regmap_init_i2c(i2c, &bd7181x_regmap_config);
|
||||
if (IS_ERR(bd7181x->regmap)) {
|
||||
ret = PTR_ERR(bd7181x->regmap);
|
||||
dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bd7181x_reg_read(bd7181x, BD7181X_REG_DEVICE);
|
||||
if(ret < 0) {
|
||||
dev_err(bd7181x->dev, "%s(): Read BD7181X_REG_DEVICE failed!\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
dev_info(bd7181x->dev, "BD7181x: Device ID=0x%X\n", ret);
|
||||
|
||||
bd7181x_irq_init(bd7181x, of_pmic_plat_data);
|
||||
|
||||
ret = mfd_add_devices(bd7181x->dev, -1,
|
||||
bd7181x_mfd_cells, ARRAY_SIZE(bd7181x_mfd_cells),
|
||||
NULL, 0,
|
||||
regmap_irq_get_domain(bd7181x->irq_data));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
bd7181x_hw_init(bd7181x);
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
mfd_remove_devices(bd7181x->dev);
|
||||
kfree(bd7181x);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @brief remove bd7181x device
|
||||
* @param i2c client object provided by system
|
||||
* @return 0
|
||||
*/
|
||||
static int bd7181x_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct bd7181x *bd7181x = i2c_get_clientdata(i2c);
|
||||
|
||||
bd7181x_irq_exit(bd7181x);
|
||||
mfd_remove_devices(bd7181x->dev);
|
||||
kfree(bd7181x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bd7181x_i2c_id[] = {
|
||||
{ "bd71815", 0 },
|
||||
{ "bd71817", 1 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bd7181x_i2c_id);
|
||||
|
||||
static int bd7181x_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct bd7181x *bd7181x = i2c_get_clientdata(i2c);
|
||||
|
||||
/* Set LPSR_MODE bit to get into LPSR state when PWRON goes low */
|
||||
bd7181x_set_bits(bd7181x, BD7181X_REG_PWRCTRL, BIT(4));
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd7181x_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct bd7181x *bd7181x = i2c_get_clientdata(i2c);
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
/* Flip LPSR_MODE setting back to SNVS state */
|
||||
bd7181x_clear_bits(bd7181x, BD7181X_REG_PWRCTRL, BIT(4));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SIMPLE_DEV_PM_OPS(bd7181x_pm_ops, bd7181x_suspend, bd7181x_resume);
|
||||
|
||||
static struct i2c_driver bd7181x_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bd7181x",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(bd7181x_of_match),
|
||||
.pm = &bd7181x_pm_ops,
|
||||
},
|
||||
.probe = bd7181x_i2c_probe,
|
||||
.remove = bd7181x_i2c_remove,
|
||||
.id_table = bd7181x_i2c_id,
|
||||
};
|
||||
|
||||
static int __init bd7181x_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bd7181x_i2c_driver);
|
||||
}
|
||||
/* init early so consumer devices can complete system boot */
|
||||
subsys_initcall(bd7181x_i2c_init);
|
||||
|
||||
static void __exit bd7181x_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bd7181x_i2c_driver);
|
||||
}
|
||||
module_exit(bd7181x_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Tony Luo <luofc@embest-tech.com>");
|
||||
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
|
||||
MODULE_DESCRIPTION("BD71815/BD71817 chip multi-function driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Maxim MAX77818 MFD Core
|
||||
*
|
||||
* Copyright (C) 2014 Maxim Integrated Product
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
* Author: Shawn Guo <shawn.guo@linaro.org>
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This driver is based on max77843.c
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/max77818/max77818.h>
|
||||
#include <linux/power/max17042_battery.h>
|
||||
#include <linux/power/max77818_battery_utils.h>
|
||||
|
||||
#define I2C_ADDR_PMIC (0xcc >> 1) /* PMIC (CLOGIC/SAFELDOs) */
|
||||
#define I2C_ADDR_CHARGER (0xd2 >> 1) /* Charger */
|
||||
#define I2C_ADDR_FUEL_GAUGE (0x6c >> 1) /* Fuel Gauge */
|
||||
|
||||
#define REG_PMICID 0x20
|
||||
#define REG_PMICREV 0x21
|
||||
|
||||
static int max77818_chg_handle_pre_irq(void *irq_drv_data)
|
||||
{
|
||||
struct max77818_dev *max77818_dev = (struct max77818_dev*) irq_drv_data;
|
||||
bool restore_state = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!max77818_dev) {
|
||||
printk("%s: No driver data, unable to disable FGCC\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = MAX77818_START_NON_FGCC_OP(
|
||||
max77818_dev,
|
||||
restore_state,
|
||||
"Disabling FGCC before handling charger interrupt");
|
||||
if (ret)
|
||||
dev_err(max77818_dev->dev,
|
||||
"Failed to disable FGCC\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77818_chg_handle_post_irq(void *irq_drv_data)
|
||||
{
|
||||
struct max77818_dev *max77818_dev = (struct max77818_dev*) irq_drv_data;
|
||||
bool restore_state = 1;
|
||||
int ret = 0;
|
||||
|
||||
if (!max77818_dev) {
|
||||
printk("%s: No driver data, unable to disable FGCC\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = MAX77818_FINISH_NON_FGCC_OP(
|
||||
max77818_dev,
|
||||
restore_state,
|
||||
"Enabling FGCC after handling charger interrupt");
|
||||
if (ret)
|
||||
dev_err(max77818_dev->dev,
|
||||
"Failed to enable FGCC\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct regmap_config max77818_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static const struct regmap_config max77818_regmap_config_fg = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.val_format_endian = REGMAP_ENDIAN_NATIVE,
|
||||
};
|
||||
|
||||
/* Declare Interrupt */
|
||||
static const struct regmap_irq max77818_intsrc_irqs[] = {
|
||||
{ .reg_offset = 0, .mask = BIT_CHGR_INT, },
|
||||
{ .reg_offset = 0, .mask = BIT_FG_INT, },
|
||||
{ .reg_offset = 0, .mask = BIT_SYS_INT, },
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77818_intsrc_irq_chip = {
|
||||
.name = "max77818 intsrc",
|
||||
.status_base = REG_INTSRC,
|
||||
.mask_base = REG_INTSRCMASK,
|
||||
.num_regs = 1,
|
||||
.irqs = max77818_intsrc_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77818_intsrc_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77818_sys_irqs[] = {
|
||||
{ .reg_offset = 0, .mask = BIT_SYSUVLO_INT, },
|
||||
{ .reg_offset = 0, .mask = BIT_SYSOVLO_INT, },
|
||||
{ .reg_offset = 0, .mask = BIT_TSHDN_INT, },
|
||||
{ .reg_offset = 0, .mask = BIT_TM_INT, },
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77818_sys_irq_chip = {
|
||||
.name = "max77818 system",
|
||||
.status_base = REG_SYSINTSRC,
|
||||
.mask_base = REG_SYSINTMASK,
|
||||
.num_regs = 1,
|
||||
.irqs = max77818_sys_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77818_sys_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77818_chg_irqs[] = {
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_BYP_I, },
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_BATP_I, },
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_BAT_I, },
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_CHG_I, },
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_WCIN_I, },
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_CHGIN_I, },
|
||||
{ .reg_offset = 0, .mask = BIT_CHG_AICL_I, },
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip max77818_chg_irq_chip = {
|
||||
.name = "max77818 chg",
|
||||
.status_base = REG_CHARGER_INT,
|
||||
.mask_base = REG_CHARGER_INT_MASK,
|
||||
.num_regs = 1,
|
||||
.irqs = max77818_chg_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77818_chg_irqs),
|
||||
.handle_pre_irq = max77818_chg_handle_pre_irq,
|
||||
.handle_post_irq = max77818_chg_handle_post_irq,
|
||||
};
|
||||
|
||||
static struct mfd_cell max77818_devices[] = {
|
||||
{
|
||||
.name = MAX77818_REGULATOR_NAME,
|
||||
.of_compatible = "maxim,"MAX77818_REGULATOR_NAME,
|
||||
}, {
|
||||
.name = MAX77818_CHARGER_NAME,
|
||||
.of_compatible = "maxim,"MAX77818_CHARGER_NAME,
|
||||
}, {
|
||||
.name = MAX77818_FUELGAUGE_NAME,
|
||||
.of_compatible = "maxim,"MAX77818_FUELGAUGE_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int max77818_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max77818_dev *me;
|
||||
u32 chip_id, chip_rev;
|
||||
int ret;
|
||||
|
||||
me = devm_kzalloc(&client->dev, sizeof(*me), GFP_KERNEL);
|
||||
if (!me)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, me);
|
||||
|
||||
mutex_init(&me->lock);
|
||||
me->dev = &client->dev;
|
||||
me->irq = client->irq;
|
||||
me->pmic = client;
|
||||
|
||||
me->regmap_pmic = devm_regmap_init_i2c(client, &max77818_regmap_config);
|
||||
if (IS_ERR(me->regmap_pmic)) {
|
||||
ret = PTR_ERR(me->regmap_pmic);
|
||||
dev_err(me->dev, "failed to initialize PMIC regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(me->regmap_pmic, REG_PMICID, &chip_id);
|
||||
if (ret < 0) {
|
||||
dev_err(me->dev, "failed to read chip id: %d\n", ret);
|
||||
return ret;
|
||||
} else {
|
||||
regmap_read(me->regmap_pmic, REG_PMICREV, &chip_rev);
|
||||
dev_info(me->dev, "device ID: 0x%x, REV: 0x%x\n",
|
||||
chip_id, chip_rev);
|
||||
}
|
||||
|
||||
me->chg = i2c_new_dummy(client->adapter, I2C_ADDR_CHARGER);
|
||||
if (!me->chg) {
|
||||
dev_err(me->dev, "failed to allocate I2C device for CHG\n");
|
||||
return ret;
|
||||
}
|
||||
i2c_set_clientdata(me->chg, me);
|
||||
|
||||
me->fg = i2c_new_dummy(client->adapter, I2C_ADDR_FUEL_GAUGE);
|
||||
if (!me->fg) {
|
||||
dev_err(me->dev, "failed to allocate I2C device for FG\n");
|
||||
goto unreg_chg;
|
||||
}
|
||||
i2c_set_clientdata(me->fg, me);
|
||||
|
||||
me->regmap_chg = devm_regmap_init_i2c(me->chg, &max77818_regmap_config);
|
||||
if (IS_ERR_OR_NULL(me->regmap_chg)) {
|
||||
ret = PTR_ERR(me->regmap_chg);
|
||||
dev_warn(me->dev, "failed to initialize CHG regmap: %d\n", ret);
|
||||
goto unreg_fg;
|
||||
}
|
||||
|
||||
me->regmap_fg= devm_regmap_init_i2c(me->fg, &max77818_regmap_config_fg);
|
||||
if (IS_ERR_OR_NULL(me->regmap_fg)) {
|
||||
ret = PTR_ERR(me->regmap_fg);
|
||||
dev_err(me->dev, "failed to initialize FG regmap: %d\n", ret);
|
||||
goto unreg_fg;
|
||||
}
|
||||
|
||||
/* Disable all interrupt source */
|
||||
regmap_write(me->regmap_pmic, REG_INTSRCMASK, 0xff);
|
||||
|
||||
/* Register overall interrupt source (sys, fg, chg) */
|
||||
ret = regmap_add_irq_chip(me->regmap_pmic, me->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED, 0,
|
||||
&max77818_intsrc_irq_chip, &me->irqc_intsrc);
|
||||
if (ret) {
|
||||
dev_err(me->dev, "failed to add intsrc irq chip: %d\n", ret);
|
||||
goto unreg_fg;
|
||||
}
|
||||
|
||||
/* Register system chip irq */
|
||||
ret = regmap_add_irq_chip(me->regmap_pmic, me->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED, 0,
|
||||
&max77818_sys_irq_chip, &me->irqc_sys);
|
||||
if (ret) {
|
||||
dev_err(me->dev, "failed to add system irq chip: %d\n", ret);
|
||||
goto del_irqc_intsrc;
|
||||
}
|
||||
|
||||
/* Register charger chip irq */
|
||||
max77818_chg_irq_chip.irq_drv_data = me;
|
||||
ret = MAX77818_DO_NON_FGCC_OP(
|
||||
me,
|
||||
regmap_add_irq_chip(me->regmap_chg,
|
||||
me->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
0,
|
||||
&max77818_chg_irq_chip,
|
||||
&me->irqc_chg),
|
||||
"adding charger chip irq\n");
|
||||
|
||||
if (ret) {
|
||||
dev_warn(me->dev, "failed to add chg irq chip: %d\n", ret);
|
||||
goto del_irqc_sys;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(me->dev);
|
||||
|
||||
ret = mfd_add_devices(me->dev, -1, max77818_devices,
|
||||
ARRAY_SIZE(max77818_devices), NULL, 0, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(me->dev, "failed to add mfd devices: %d\n", ret);
|
||||
goto del_irqc_chg;
|
||||
}
|
||||
|
||||
device_init_wakeup(me->dev, true);
|
||||
|
||||
return 0;
|
||||
|
||||
del_irqc_chg:
|
||||
regmap_del_irq_chip(me->irq, me->irqc_chg);
|
||||
del_irqc_sys:
|
||||
regmap_del_irq_chip(me->irq, me->irqc_sys);
|
||||
del_irqc_intsrc:
|
||||
regmap_del_irq_chip(me->irq, me->irqc_intsrc);
|
||||
unreg_fg:
|
||||
i2c_unregister_device(me->fg);
|
||||
unreg_chg:
|
||||
i2c_unregister_device(me->chg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77818_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max77818_dev *me = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(me->dev);
|
||||
|
||||
regmap_del_irq_chip(me->irq, me->irqc_chg);
|
||||
regmap_del_irq_chip(me->irq, me->irqc_sys);
|
||||
regmap_del_irq_chip(me->irq, me->irqc_intsrc);
|
||||
|
||||
i2c_unregister_device(me->fg);
|
||||
|
||||
if (me->chg) {
|
||||
i2c_unregister_device(me->chg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77818_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct max77818_dev *me = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
enable_irq_wake(me->irq);
|
||||
disable_irq(me->irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77818_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct max77818_dev *me = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
disable_irq_wake(me->irq);
|
||||
enable_irq(me->irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max77818_pm, max77818_suspend, max77818_resume);
|
||||
|
||||
static struct of_device_id max77818_of_id[] = {
|
||||
{ .compatible = "maxim,max77818" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max77818_of_id);
|
||||
|
||||
static const struct i2c_device_id max77818_i2c_id[] = {
|
||||
{ "max77818" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max77818_i2c_id);
|
||||
|
||||
static struct i2c_driver max77818_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max77818",
|
||||
.pm = &max77818_pm,
|
||||
.of_match_table = max77818_of_id,
|
||||
},
|
||||
.probe = max77818_i2c_probe,
|
||||
.remove = max77818_i2c_remove,
|
||||
.id_table = max77818_i2c_id,
|
||||
};
|
||||
module_i2c_driver(max77818_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MAX77818 MFD Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* MFD driver for SY7636A chip
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Based on the lp87565 driver by Keerthy <j-keerthy@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/mfd/sy7636a.h>
|
||||
|
||||
static const struct regmap_config sy7636a_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static const struct mfd_cell sy7636a_cells[] = {
|
||||
{ .name = "sy7636a-regulator", },
|
||||
{ .name = "sy7636a-temperature", },
|
||||
{ .name = "sy7636a-thermal", },
|
||||
};
|
||||
|
||||
static const struct of_device_id of_sy7636a_match_table[] = {
|
||||
{ .compatible = "silergy,sy7636a", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_sy7636a_match_table);
|
||||
|
||||
static const char *states[] = {
|
||||
"no fault event",
|
||||
"UVP at VP rail",
|
||||
"UVP at VN rail",
|
||||
"UVP at VPOS rail",
|
||||
"UVP at VNEG rail",
|
||||
"UVP at VDDH rail",
|
||||
"UVP at VEE rail",
|
||||
"SCP at VP rail",
|
||||
"SCP at VN rail",
|
||||
"SCP at VPOS rail",
|
||||
"SCP at VNEG rail",
|
||||
"SCP at VDDH rail",
|
||||
"SCP at VEE rail",
|
||||
"SCP at V COM rail",
|
||||
"UVLO",
|
||||
"Thermal shutdown",
|
||||
};
|
||||
|
||||
int get_vcom_voltage_mv(struct regmap *regmap)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val, val_h;
|
||||
|
||||
ret = regmap_read(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_L, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_H, &val_h);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val |= (val_h << 8);
|
||||
|
||||
return (val & 0x1FF) * 10;
|
||||
}
|
||||
|
||||
int set_vcom_voltage_mv(struct regmap *regmap, unsigned int vcom)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
if (vcom < 0 || vcom > 5000)
|
||||
return -EINVAL;
|
||||
|
||||
val = (unsigned int)(vcom / 10) & 0x1ff;
|
||||
|
||||
ret = regmap_write(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_L, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_H, val >> 8);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(dev);
|
||||
|
||||
ret = regmap_read(sy7636a->regmap, SY7636A_REG_FAULT_FLAG, &val);
|
||||
if (ret) {
|
||||
dev_err(sy7636a->dev, "Failed to read from device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = val >> 1;
|
||||
|
||||
if (val >= ARRAY_SIZE(states)) {
|
||||
dev_err(sy7636a->dev, "Unexpected value read from device: %u\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", states[val]);
|
||||
}
|
||||
static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
|
||||
|
||||
static ssize_t powergood_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(dev);
|
||||
|
||||
ret = regmap_read(sy7636a->regmap, SY7636A_REG_FAULT_FLAG, &val);
|
||||
if (ret) {
|
||||
dev_err(sy7636a->dev, "Failed to read from device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val &= 0x01;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", val ? "ON" : "OFF");
|
||||
}
|
||||
static DEVICE_ATTR(power_good, S_IRUGO, powergood_show, NULL);
|
||||
|
||||
static ssize_t vcom_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(dev);
|
||||
|
||||
ret = get_vcom_voltage_mv(sy7636a->regmap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", -ret);
|
||||
}
|
||||
|
||||
static ssize_t vcom_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int vcom;
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(dev);
|
||||
|
||||
ret = kstrtoint(buf, 0, &vcom);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vcom > 0 || vcom < -5000)
|
||||
return -EINVAL;
|
||||
|
||||
ret = set_vcom_voltage_mv(sy7636a->regmap, (unsigned int)(-vcom));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(vcom, S_IRUGO | S_IWUSR, vcom_show, vcom_store);
|
||||
|
||||
static struct attribute *sy7636a_sysfs_attrs[] = {
|
||||
&dev_attr_state.attr,
|
||||
&dev_attr_power_good.attr,
|
||||
&dev_attr_vcom.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group sy7636a_sysfs_attr_group = {
|
||||
.attrs = sy7636a_sysfs_attrs,
|
||||
};
|
||||
|
||||
static int sy7636a_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *ids)
|
||||
{
|
||||
struct sy7636a *sy7636a;
|
||||
int ret;
|
||||
|
||||
sy7636a = devm_kzalloc(&client->dev, sizeof(struct sy7636a), GFP_KERNEL);
|
||||
if (sy7636a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
sy7636a->dev = &client->dev;
|
||||
|
||||
sy7636a->regmap = devm_regmap_init_i2c(client, &sy7636a_regmap_config);
|
||||
if (IS_ERR(sy7636a->regmap)) {
|
||||
ret = PTR_ERR(sy7636a->regmap);
|
||||
dev_err(sy7636a->dev,
|
||||
"Failed to initialize register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, sy7636a);
|
||||
|
||||
ret = sysfs_create_group(&client->dev.kobj, &sy7636a_sysfs_attr_group);
|
||||
if (ret) {
|
||||
dev_err(sy7636a->dev, "Failed to create sysfs attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(sy7636a->dev, PLATFORM_DEVID_AUTO,
|
||||
sy7636a_cells, ARRAY_SIZE(sy7636a_cells),
|
||||
NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(sy7636a->dev, "Failed to add mfd devices\n");
|
||||
sysfs_remove_group(&client->dev.kobj, &sy7636a_sysfs_attr_group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sy7636a_id_table[] = {
|
||||
{ "sy7636a", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sy7636a_id_table);
|
||||
|
||||
static struct i2c_driver sy7636a_driver = {
|
||||
.driver = {
|
||||
.name = "sy7636a",
|
||||
.of_match_table = of_sy7636a_match_table,
|
||||
},
|
||||
.probe = sy7636a_probe,
|
||||
.id_table = sy7636a_id_table,
|
||||
};
|
||||
module_i2c_driver(sy7636a_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>");
|
||||
MODULE_DESCRIPTION("Silergy SY7636A Multi-Function Device Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -501,4 +501,5 @@ source "drivers/misc/cxl/Kconfig"
|
|||
source "drivers/misc/ocxl/Kconfig"
|
||||
source "drivers/misc/cardreader/Kconfig"
|
||||
source "drivers/misc/habanalabs/Kconfig"
|
||||
source "drivers/misc/rm-otgcontrol/Kconfig"
|
||||
endmenu
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# Makefile for misc devices that really don't fit anywhere else.
|
||||
#
|
||||
|
||||
obj-${CONFIG_RM_OTGCONTROL} += rm-otgcontrol/
|
||||
obj-$(CONFIG_IBM_ASM) += ibmasm/
|
||||
obj-$(CONFIG_IBMVMC) += ibmvmc.o
|
||||
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for reMarkable OTG Control
|
||||
#
|
||||
|
||||
config RM_OTGCONTROL
|
||||
tristate "reMarkable OTG control"
|
||||
help
|
||||
Add reMarkable OTG control module, required to control access to/from external equipment
|
||||
|
||||
config RM_OTGCONTROL_DEBUG
|
||||
bool "reMarkable OTG control debug"
|
||||
depends on RM_OTGCONTROL
|
||||
help
|
||||
"Enable extensive debug output (useful when debugging FSM type of errors to see what is going on)"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
obj-${CONFIG_RM_OTGCONTROL} += otgcontrol.o
|
||||
otgcontrol-objs := otgcontrol_main.o
|
||||
otgcontrol-objs += otgcontrol_sysfs.o
|
||||
otgcontrol-objs += otgcontrol_fsm.o
|
||||
otgcontrol-objs += otgcontrol_onewire.o
|
||||
otgcontrol-objs += otgcontrol_charging_ctrl.o
|
||||
otgcontrol-objs += otgcontrol_dr_mode.o
|
||||
|
||||
ccflags-$(CONFIG_RM_OTGCONTROL_DEBUG) += -DDEBUG
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __RM_OTGCONTROL_H_
|
||||
#define __RM_OTGCONTROL_H_
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/extcon.h>
|
||||
|
||||
#define SYNC_SET_FLAG(flag, lock) ( \
|
||||
{ \
|
||||
mutex_lock(lock); \
|
||||
flag = true; \
|
||||
mutex_unlock(lock); \
|
||||
} \
|
||||
)
|
||||
|
||||
#define SYNC_CLEAR_FLAG(flag, lock) ( \
|
||||
{ \
|
||||
mutex_lock(lock); \
|
||||
flag = false; \
|
||||
mutex_unlock(lock); \
|
||||
} \
|
||||
)
|
||||
|
||||
#define SYNC_GET_FLAG(flag, lock) ( \
|
||||
{ \
|
||||
bool state; \
|
||||
mutex_lock(lock); \
|
||||
state = flag; \
|
||||
mutex_unlock(lock); \
|
||||
state; \
|
||||
} \
|
||||
)
|
||||
|
||||
struct rm_otgcontrol_platform_data {
|
||||
/* Reference to charger driver for OTG power control */
|
||||
struct power_supply *vbus_supply;
|
||||
|
||||
/* One-wire tty device and gpio config */
|
||||
const char *one_wire_tty_name;
|
||||
struct gpio_desc *one_wire_gpio;
|
||||
int one_wire_gpio_irq;
|
||||
};
|
||||
|
||||
struct rm_otgcontrol_data {
|
||||
struct device *dev;
|
||||
struct rm_otgcontrol_platform_data *pdata;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
struct extcon_dev *extcon_dev;
|
||||
|
||||
unsigned long one_wire_gpio_debounce_jiffies;
|
||||
struct delayed_work one_wire_gpio_irq_work_queue;
|
||||
bool one_wire_gpio_irq_is_active;
|
||||
int one_wire_gpio_state;
|
||||
bool one_wire_gpio_irq_is_handling;
|
||||
|
||||
int otg_controlstate;
|
||||
int mode_requested;
|
||||
|
||||
struct pinctrl* one_wire_pinctrl;
|
||||
struct pinctrl_state* one_wire_pinctrl_states[3];
|
||||
|
||||
struct kobject* kobject;
|
||||
|
||||
struct kobj_attribute otg1_device_connected_attribute;
|
||||
bool otg1_device_connected;
|
||||
|
||||
struct kobj_attribute otg1_dr_mode_attribute;
|
||||
int otg1_dr_mode;
|
||||
|
||||
struct kobj_attribute otg1_chargermode_attribute;
|
||||
int otg1_chargermode;
|
||||
|
||||
struct kobj_attribute otg1_controllermode_attribute;
|
||||
int otg1_controllermode;
|
||||
|
||||
struct kobj_attribute otg1_pinctrlstate_attribute;
|
||||
int otg1_pinctrlstate;
|
||||
};
|
||||
|
||||
#endif /* __RM_OTGCONTROL_H */
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "otgcontrol_charging_ctrl.h"
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
/* Copy of the structure defined in
|
||||
* ../drivers/power/supply/power_supply_sysfs.c
|
||||
*
|
||||
* As the power supply API does not define any
|
||||
* methods to access the defined enum strings for
|
||||
* the various properties, a local version is defined
|
||||
* here, as this driver also defines corresponding enumerations locally
|
||||
*/
|
||||
static const char * const charger_mode_string_list[] = {
|
||||
"Charger", "OTG Supply", "Off"
|
||||
};
|
||||
|
||||
int otgcontrol_get_otg_charger_modes(struct rm_otgcontrol_data *otgc_data,
|
||||
char *prop_buf)
|
||||
{
|
||||
int i;
|
||||
int cur_pos = 0;
|
||||
|
||||
for(i = 0;i < ARRAY_SIZE(charger_mode_string_list);i++) {
|
||||
sprintf(&prop_buf[cur_pos],
|
||||
"%s%s",
|
||||
(i > 0) ? " " : "",
|
||||
charger_mode_string_list[i]);
|
||||
|
||||
cur_pos = strlen(prop_buf);
|
||||
}
|
||||
sprintf(&prop_buf[cur_pos], "\n");
|
||||
return strlen(prop_buf);
|
||||
}
|
||||
|
||||
int otgcontrol_change_otg_charger_mode_int(struct rm_otgcontrol_data *otgc_data,
|
||||
int mode)
|
||||
{
|
||||
union power_supply_propval property_val;
|
||||
int ret;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case OTG1_CHARGERMODE_CHARGE:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting OTG1 chargermode (CHARGE)\n",
|
||||
__func__);
|
||||
|
||||
property_val.intval = POWER_SUPPLY_MODE_CHARGER;
|
||||
ret = power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_CHARGER_MODE,
|
||||
&property_val);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set charger mode\n",
|
||||
__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_CHARGERMODE_OTG:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting OTG1 chargermode (OTG)\n",
|
||||
__func__);
|
||||
property_val.intval = POWER_SUPPLY_MODE_OTG_SUPPLY;
|
||||
ret = power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_CHARGER_MODE,
|
||||
&property_val);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set charger mode\n",
|
||||
__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_CHARGERMODE_OFF:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting OTG1 chargermode (OFF)\n",
|
||||
__func__);
|
||||
property_val.intval = POWER_SUPPLY_MODE_ALL_OFF;
|
||||
ret = power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_CHARGER_MODE,
|
||||
&property_val);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set charger mode\n",
|
||||
__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Unable to set OTG1 chargermode (invalid mode %d)\n",
|
||||
__func__, mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
otgc_data->otg1_chargermode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_change_otg_charger_mode_str(struct rm_otgcontrol_data *otgc_data,
|
||||
const char *buf)
|
||||
{
|
||||
int ret, mode, i, buf_len, mode_len;
|
||||
|
||||
/* Remove trailing \n */
|
||||
if (buf[strlen(buf) - 1] == '\n')
|
||||
buf_len = strlen(buf) - 1;
|
||||
else
|
||||
buf_len = strlen(buf);
|
||||
|
||||
ret = kstrtoint(buf, 10, &mode);
|
||||
if (ret < 0) {
|
||||
mode = -1;
|
||||
for (i = 0;i < ARRAY_SIZE(charger_mode_string_list);i++) {
|
||||
mode_len = strlen(charger_mode_string_list[i]);
|
||||
if (!strncmp(charger_mode_string_list[i],
|
||||
buf,
|
||||
max(buf_len, mode_len))) {
|
||||
mode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mode < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Unable to set OTG1 chargermode "
|
||||
"(invalid mode %s)\n",
|
||||
__func__,
|
||||
buf);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
else if (mode >= ARRAY_SIZE(charger_mode_string_list)) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Unable to set OTG1 chargermode (invalid mode %d)\n",
|
||||
__func__, mode);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return otgcontrol_change_otg_charger_mode_int(otgc_data, mode);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __OTGCONTROL_CHARGING_CTRL_H__
|
||||
#define __OTGCONTROL_CHARGING_CTRL_H__
|
||||
|
||||
#include "otgcontrol.h"
|
||||
|
||||
#define OTG1_CHARGERMODE_CHARGE 0
|
||||
#define OTG1_CHARGERMODE_OTG 1
|
||||
#define OTG1_CHARGERMODE_OFF 2
|
||||
|
||||
int otgcontrol_get_otg_charger_modes(struct rm_otgcontrol_data *otgc_data,
|
||||
char *prop_buf);
|
||||
|
||||
int otgcontrol_change_otg_charger_mode_int(struct rm_otgcontrol_data *otgc_data,
|
||||
int mode);
|
||||
|
||||
int otgcontrol_change_otg_charger_mode_str(struct rm_otgcontrol_data *otgc_data,
|
||||
const char *buf);
|
||||
|
||||
#endif /* __OTGCONTROL_CHARGING_CTRL_H__ */
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "otgcontrol_dr_mode.h"
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static const unsigned int usb_extcon_cable[] = {
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
int otgcontrol_init_extcon(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Allocating extcon device\n",
|
||||
__func__);
|
||||
|
||||
otgc_data->extcon_dev = devm_extcon_dev_allocate(otgc_data->dev,
|
||||
usb_extcon_cable);
|
||||
if (IS_ERR(otgc_data->extcon_dev)) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: failed to allocate extcon device\n",
|
||||
__func__);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Registering extcon device\n",
|
||||
__func__);
|
||||
|
||||
ret = devm_extcon_dev_register(otgc_data->dev, otgc_data->extcon_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to register extcon device\n",
|
||||
__func__);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: De-allocating extcon device\n",
|
||||
__func__);
|
||||
|
||||
kfree(otgc_data->extcon_dev);
|
||||
otgc_data->extcon_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_set_dr_mode(struct rm_otgcontrol_data *otgc_data, int mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case OTG1_DR_MODE__DEVICE:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Switching OTG1 DR mode -> DEVICE\n",
|
||||
__func__);
|
||||
|
||||
ret = extcon_set_state_sync(otgc_data->extcon_dev,
|
||||
EXTCON_USB_HOST,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set OTG1 DR mode\n",
|
||||
__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_DR_MODE__HOST:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Switching OTG1 DR mode -> HOST\n",
|
||||
__func__);
|
||||
|
||||
otgc_data->otg1_dr_mode = mode;
|
||||
ret = extcon_set_state_sync(otgc_data->extcon_dev,
|
||||
EXTCON_USB_HOST,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set OTG1 DR mode\n",
|
||||
__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: unable to switch OTG1 DR mode (unknown mode %d)\n",
|
||||
__func__,
|
||||
mode);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
otgc_data->otg1_dr_mode = mode;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int otgcontrol_get_dr_mode(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
/* Just return the last stored value */
|
||||
return otgc_data->otg1_dr_mode;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __OTGCONTROL_DR_MODE_H__
|
||||
#define __OTGCONTROL_DR_MODE_H__
|
||||
|
||||
#include "otgcontrol.h"
|
||||
|
||||
#define OTG1_DR_MODE__DEVICE 0
|
||||
#define OTG1_DR_MODE__HOST 1
|
||||
|
||||
int otgcontrol_init_extcon(struct rm_otgcontrol_data *otgc_data);
|
||||
int otgcontrol_set_dr_mode(struct rm_otgcontrol_data *otgc_dta, int mode);
|
||||
int otgcontrol_get_dr_mode(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
#endif // __OTGCONTROL_DR_MODE_H__
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __OTGCONTROL_FSM_H__
|
||||
#define __OTGCONTROL_FSM_H__
|
||||
|
||||
#include "otgcontrol.h"
|
||||
|
||||
int otgcontrol_init_fsm(struct rm_otgcontrol_data *otgc_data);
|
||||
int otgcontrol_handleInput(struct rm_otgcontrol_data *otgc_data,
|
||||
int signal,
|
||||
void *param);
|
||||
|
||||
#define OTG_MODE__MANUAL_CONTROL 0
|
||||
#define OTG_MODE__ONEWIRE_AUTH 1
|
||||
#define OTG_MODE__USB_NO_AUTH 2
|
||||
|
||||
#define OTG1_STATE__MANUAL_CONTROL 0
|
||||
#define OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED 1
|
||||
#define OTG1_STATE__ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS 2
|
||||
#define OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED 3
|
||||
#define OTG1_STATE__USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE 4
|
||||
#define OTG1_STATE__USB_NO_AUTH_NOT_CONNECTED 5
|
||||
#define OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED 6
|
||||
#define OTG1_STATE__HOST_CONNECTED 7
|
||||
|
||||
#define OTG1_EVENT__CHARGER_CONNECTED 0
|
||||
#define OTG1_EVENT__CHARGER_DISCONNECTED 1
|
||||
#define OTG1_EVENT__DEVICE_CONNECTED 2
|
||||
#define OTG1_EVENT__DEVICE_DISCONNECTED 3
|
||||
#define OTG1_EVENT__CHALLENGE_REPLY_RECEIVED 4
|
||||
#define OTG1_EVENT__TIMEOUT 5
|
||||
#define OTG1_EVENT__MODE_CHANGE_REQUESTED 6
|
||||
#define OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY 7
|
||||
#define OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED 8
|
||||
#define OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED 9
|
||||
|
||||
#endif /* __OTGCONTROL_FSM_H__ */
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "otgcontrol.h"
|
||||
#include "otgcontrol_sysfs.h"
|
||||
#include "otgcontrol_fsm.h"
|
||||
#include "otgcontrol_dr_mode.h"
|
||||
#include "otgcontrol_onewire.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/extcon.h>
|
||||
|
||||
static int rm_otgcontrol_init(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating sysfs nodes\n",
|
||||
__func__);
|
||||
|
||||
ret = otgcontrol_init_sysfs_nodes(otgc_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating extcon device to control USB OTG dr mode\n",
|
||||
__func__);
|
||||
|
||||
ret = otgcontrol_init_extcon(otgc_data);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to initiate extron (%d)\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating onewire state and setting to default state "
|
||||
"(GPIO)\n",
|
||||
__func__);
|
||||
|
||||
ret = otgcontrol_init_one_wire_mux_state(otgc_data);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to initiate onewire pincontrol "
|
||||
"configuration\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating one-wire gpio irq\n",
|
||||
__func__);
|
||||
|
||||
ret = otgcontrol_init_gpio_irq(otgc_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating fsm to start in AUTHORIZED MODE !!\n",
|
||||
__func__);
|
||||
|
||||
ret = otgcontrol_init_fsm(otgc_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rm_otgcontrol_parse_dt(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
struct device *dev = otgc_data->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct rm_otgcontrol_platform_data *pdata = otgc_data->pdata;
|
||||
const char *vbus_supply_name;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Enter\n",
|
||||
__func__);
|
||||
|
||||
if (of_find_property(np, "vbus-supply", NULL)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Found vbus-supply property, "
|
||||
"trying to get get vbus powersupply by phandle\n",
|
||||
__func__);
|
||||
|
||||
pdata->vbus_supply = power_supply_get_by_phandle(np,
|
||||
"vbus-supply");
|
||||
if (IS_ERR_OR_NULL(pdata->vbus_supply)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: vbus supply not ready, defering probe\n",
|
||||
__func__);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
else if (of_find_property(np, "vbus-supply-name", NULL)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Found vbus-supply-name property, "
|
||||
"trying to read it\n",
|
||||
__func__);
|
||||
|
||||
ret = of_property_read_string(np,
|
||||
"vbus-supply-name",
|
||||
&vbus_supply_name);
|
||||
if (ret) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to read property vbus-supply-name "
|
||||
"(code %d)\n",
|
||||
__func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Read vbus-supply-name: %s, "
|
||||
"trying to get reference to it\n",
|
||||
__func__, vbus_supply_name);
|
||||
|
||||
pdata->vbus_supply = power_supply_get_by_name(vbus_supply_name);
|
||||
if (IS_ERR(pdata->vbus_supply)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: vbus supply not ready, defering probe\n",
|
||||
__func__);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Required vbus-supply-name property not given !\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Got pointer to vbus-supply\n",
|
||||
__func__);
|
||||
|
||||
if (of_find_property(np, "one-wire-tty-name", NULL)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Found one-wire-tty-name property, "
|
||||
"trying to read it\n",
|
||||
__func__);
|
||||
|
||||
ret = of_property_read_string(np,
|
||||
"one-wire-tty-name",
|
||||
&otgc_data->pdata->one_wire_tty_name);
|
||||
if (ret) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to read property one-wire-tty-name "
|
||||
"(code %d)\n",
|
||||
__func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: required property one-wire-tty-name not given !\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_find_property(np, "one-wire-gpios", NULL)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Found one-wire-gpio property, trying to read it\n",
|
||||
__func__);
|
||||
|
||||
otgc_data->pdata->one_wire_gpio = devm_gpiod_get(otgc_data->dev,
|
||||
"one-wire",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(otgc_data->pdata->one_wire_gpio)) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to read property one-wire-gpio "
|
||||
"(code %ld)\n",
|
||||
__func__,
|
||||
PTR_ERR(otgc_data->pdata->one_wire_gpio));
|
||||
return PTR_ERR(otgc_data->pdata->one_wire_gpio);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: required property one-wire-gpio not given !\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_otgcontrol_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data;
|
||||
struct rm_otgcontrol_platform_data *pdata;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: rM OTGCONTROL Driver Loading\n",
|
||||
__func__);
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: Allocating otgcontrol_data\n",
|
||||
__func__);
|
||||
|
||||
otgc_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct rm_otgcontrol_data),
|
||||
GFP_KERNEL);
|
||||
if (!otgc_data) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Failed to allocate otgc_data\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: Allocating otgcontrol_data\n",
|
||||
__func__);
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct rm_otgcontrol_platform_data),
|
||||
GFP_KERNEL);
|
||||
if (unlikely(!pdata)) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Failed to allocate pdata\n",
|
||||
__func__);
|
||||
pdata = ERR_PTR(-ENOMEM);
|
||||
ret = -ENOMEM;
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
otgc_data->dev = &pdev->dev;
|
||||
otgc_data->pdata = pdata;
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: Reading platform data from devicetree\n",
|
||||
__func__);
|
||||
|
||||
ret = rm_otgcontrol_parse_dt(otgc_data);
|
||||
if (ret < 0) {
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
dev_info(&pdev->dev,
|
||||
"%s: Defering probe due to charger driver not being"
|
||||
"loaded/available yet\n",
|
||||
__func__);
|
||||
}
|
||||
else {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Failed to load platform data from devicetree, "
|
||||
"code %d\n",
|
||||
__func__,
|
||||
ret);
|
||||
}
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: Setting otgc_data reference in pdev, and initiating\n",
|
||||
__func__);
|
||||
|
||||
ret = rm_otgcontrol_init(otgc_data);
|
||||
if(ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Failed to init otgcontrol, "
|
||||
"code %d\n",
|
||||
__func__,
|
||||
ret);
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, otgc_data);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"Loaded successfully !\n");
|
||||
|
||||
return 0;
|
||||
|
||||
error_2:
|
||||
otgcontrol_uninit_sysfs_nodes(otgc_data);
|
||||
otgcontrol_uninit_gpio_irq(otgc_data);
|
||||
|
||||
error_1:
|
||||
/* No need to do explicit calls to devm_kfree */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rm_otgcontrol_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data = platform_get_drvdata(pdev);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Un-initializing sysfs nodes\n",
|
||||
__func__);
|
||||
otgcontrol_uninit_sysfs_nodes(otgc_data);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Un-initialize gpio irq\n",
|
||||
__func__);
|
||||
otgcontrol_uninit_gpio_irq(otgc_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined CONFIG_PM
|
||||
static int rm_otgcontrol_suspend(struct device *dev)
|
||||
{
|
||||
dev_dbg(dev, "%s Enter:\n", __func__);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_otgcontrol_resume(struct device *dev)
|
||||
{
|
||||
dev_dbg(dev, "%s Enter:\n", __func__);
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define rm_otgcontrol_suspend NULL
|
||||
#define rm_otgcontrol_resume NULL
|
||||
#endif
|
||||
|
||||
static struct of_device_id rm_otgcontrol_dt_ids[] = {
|
||||
{ .compatible = "rm-otgcontrol" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rm_otgcontrol_dt_ids);
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rm_otgcontrol_pm_ops,
|
||||
rm_otgcontrol_suspend,
|
||||
rm_otgcontrol_resume);
|
||||
|
||||
static struct platform_driver rm_otgcontrol_driver = {
|
||||
.driver = {
|
||||
.name = "rm_otg_control",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &rm_otgcontrol_pm_ops,
|
||||
#endif
|
||||
.of_match_table = rm_otgcontrol_dt_ids,
|
||||
},
|
||||
.probe = rm_otgcontrol_probe,
|
||||
.remove = rm_otgcontrol_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rm_otgcontrol_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("reMarkable OTG control driver, to enable authentication of "
|
||||
"devices connecting through the USB OTG interface");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_AUTHOR("Steinar Bakkemo <steinar.bakkemo@remarkable.no");
|
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "otgcontrol_onewire.h"
|
||||
#include "otgcontrol_fsm.h"
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define ONE_WIRE_GPIO_DEBOUNCE_MS 500 /* ms */
|
||||
|
||||
int otgcontrol_init_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating one-wire pinctrl states\n",
|
||||
__func__);
|
||||
|
||||
otgc_data->one_wire_pinctrl = devm_pinctrl_get(otgc_data->dev);
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl)) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to get pinctrl\n",
|
||||
__func__);
|
||||
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl);
|
||||
}
|
||||
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO] =
|
||||
pinctrl_lookup_state(otgc_data->one_wire_pinctrl,
|
||||
"default");
|
||||
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO])) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to configure one-wire-gpio state\n",
|
||||
__func__);
|
||||
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO]);
|
||||
}
|
||||
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX] =
|
||||
pinctrl_lookup_state(otgc_data->one_wire_pinctrl,
|
||||
"one_wire_uart_tx");
|
||||
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX])) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to configure one-wire-uart-tx state\n",
|
||||
__func__);
|
||||
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX]);
|
||||
}
|
||||
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX] =
|
||||
pinctrl_lookup_state(otgc_data->one_wire_pinctrl,
|
||||
"one_wire_uart_rx");
|
||||
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX])) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to configure one-wire-uart-rx\n",
|
||||
__func__);
|
||||
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX]);
|
||||
}
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting default state (GPIO)\n",
|
||||
__func__);
|
||||
|
||||
ret = pinctrl_select_state(
|
||||
otgc_data->one_wire_pinctrl,
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO]);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set default state (GPIO)\n",
|
||||
__func__);
|
||||
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
}
|
||||
|
||||
otgc_data->otg1_pinctrlstate = OTG1_ONEWIRE_STATE__GPIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_switch_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data,
|
||||
int state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case OTG1_ONEWIRE_STATE__GPIO:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Switching onewire state -> GPIO\n",
|
||||
__func__);
|
||||
|
||||
ret = pinctrl_select_state(
|
||||
otgc_data->one_wire_pinctrl,
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO]);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set pinctrl state\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_ONEWIRE_STATE__UART_RX:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Switching onewire state -> UART RX\n",
|
||||
__func__);
|
||||
|
||||
ret = pinctrl_select_state(
|
||||
otgc_data->one_wire_pinctrl,
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX]);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set pinctrl state\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_ONEWIRE_STATE__UART_TX:
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: switching onewire state -> UART TX\n",
|
||||
__func__);
|
||||
|
||||
ret = pinctrl_select_state(
|
||||
otgc_data->one_wire_pinctrl,
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX]);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to set pinctrl state\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: unable to switch onewire state (unknown state %d)\n",
|
||||
__func__, state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
otgc_data->otg1_pinctrlstate = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_get_current_gpio_state(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
return SYNC_GET_FLAG(gpiod_get_raw_value(otgc_data->pdata->one_wire_gpio),
|
||||
&otgc_data->lock);
|
||||
}
|
||||
|
||||
const char *otgcontrol_gpio_state_name(int state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case OTG1_ONEWIRE_GPIO_STATE__DEVICE_CONNECTED:
|
||||
return "DEVICE CONNECTED";
|
||||
break;
|
||||
case OTG1_ONEWIRE_GPIO_STATE__DEVICE_NOT_CONNECTED:
|
||||
return "DEVICE NOT CONNECTED";
|
||||
break;
|
||||
default:
|
||||
return "UNKNOWN DEVICE CONNECTION STATE EXPECTED 0 or 1)";
|
||||
}
|
||||
}
|
||||
|
||||
int otgcontrol_init_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting local gpio debounce jiffies\n",
|
||||
__func__);
|
||||
|
||||
otgc_data->one_wire_gpio_debounce_jiffies =
|
||||
msecs_to_jiffies(ONE_WIRE_GPIO_DEBOUNCE_MS);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Initiating irq worker\n",
|
||||
__func__);
|
||||
|
||||
INIT_DELAYED_WORK(&otgc_data->one_wire_gpio_irq_work_queue,
|
||||
otgcontrol_gpio_irq_work);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Getting IRQ from given gpio (%d)\n",
|
||||
__func__,
|
||||
desc_to_gpio(otgc_data->pdata->one_wire_gpio));
|
||||
|
||||
otgc_data->pdata->one_wire_gpio_irq = gpiod_to_irq(otgc_data->pdata->one_wire_gpio);
|
||||
if (otgc_data->pdata->one_wire_gpio_irq < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to get irq for given gpio (%d)\n",
|
||||
__func__,
|
||||
desc_to_gpio(otgc_data->pdata->one_wire_gpio));
|
||||
return otgc_data->pdata->one_wire_gpio_irq;
|
||||
}
|
||||
|
||||
otgc_data->one_wire_gpio_state =
|
||||
otgcontrol_get_current_gpio_state(otgc_data);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Current GPIO state: %s\n",
|
||||
__func__,
|
||||
otgcontrol_gpio_state_name(otgc_data->one_wire_gpio_state));
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Clearing is-handling flag\n",
|
||||
__func__);
|
||||
|
||||
otgc_data->one_wire_gpio_irq_is_handling = false;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Requesting threaded irq\n",
|
||||
__func__);
|
||||
|
||||
ret = devm_request_threaded_irq(
|
||||
otgc_data->dev,
|
||||
otgc_data->pdata->one_wire_gpio_irq,
|
||||
NULL,
|
||||
otgcontrol_gpio_irq_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"one_wire_gpio_irq",
|
||||
otgc_data);
|
||||
if (ret < 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to request handler for IRQ (%d) "
|
||||
"given for GPIO (%d)\n",
|
||||
__func__,
|
||||
otgc_data->pdata->one_wire_gpio_irq,
|
||||
desc_to_gpio(otgc_data->pdata->one_wire_gpio));
|
||||
return ret;
|
||||
}
|
||||
mutex_lock(&otgc_data->lock);
|
||||
otgc_data->one_wire_gpio_irq_is_active = true;
|
||||
mutex_unlock(&otgc_data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void otgcontrol_uninit_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
cancel_delayed_work_sync(&otgc_data->one_wire_gpio_irq_work_queue);
|
||||
}
|
||||
|
||||
void otgcontrol_activate_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
if (!otgc_data->one_wire_gpio_irq_is_active) {
|
||||
enable_irq(otgc_data->pdata->one_wire_gpio_irq);
|
||||
otgc_data->one_wire_gpio_irq_is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
void otgcontrol_deactivate_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
if (otgc_data->one_wire_gpio_irq_is_active) {
|
||||
disable_irq(otgc_data->pdata->one_wire_gpio_irq);
|
||||
otgc_data->one_wire_gpio_irq_is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t otgcontrol_gpio_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data = (struct rm_otgcontrol_data*)data;
|
||||
|
||||
if (SYNC_GET_FLAG(otgc_data->one_wire_gpio_irq_is_handling,
|
||||
&otgc_data->lock)) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Is already handling irq, ignoring this\n",
|
||||
__func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
else {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Queueing IRQ handling for execution in %d ms\n",
|
||||
__func__,
|
||||
ONE_WIRE_GPIO_DEBOUNCE_MS);
|
||||
|
||||
SYNC_SET_FLAG(otgc_data->one_wire_gpio_irq_is_handling,
|
||||
&otgc_data->lock);
|
||||
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&otgc_data->one_wire_gpio_irq_work_queue,
|
||||
otgc_data->one_wire_gpio_debounce_jiffies);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void otgcontrol_gpio_irq_work(struct work_struct *work)
|
||||
{
|
||||
int cur_gpio_state;
|
||||
|
||||
struct delayed_work *delayed_work =
|
||||
container_of(work,
|
||||
struct delayed_work,
|
||||
work);
|
||||
|
||||
struct rm_otgcontrol_data *otgc_data =
|
||||
container_of(delayed_work,
|
||||
struct rm_otgcontrol_data,
|
||||
one_wire_gpio_irq_work_queue);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Checking current gpio state\n",
|
||||
__func__);
|
||||
|
||||
cur_gpio_state = otgcontrol_get_current_gpio_state(otgc_data);
|
||||
if (cur_gpio_state != otgc_data->one_wire_gpio_state) {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: GPIO state changed -> %s\n",
|
||||
__func__,
|
||||
otgcontrol_gpio_state_name(cur_gpio_state));
|
||||
|
||||
otgc_data->one_wire_gpio_state = cur_gpio_state;
|
||||
|
||||
if (otgc_data->one_wire_gpio_state ==
|
||||
OTG1_ONEWIRE_GPIO_STATE__DEVICE_CONNECTED) {
|
||||
SYNC_SET_FLAG(otgc_data->otg1_device_connected,
|
||||
&otgc_data->lock);
|
||||
|
||||
otgcontrol_handleInput(otgc_data,
|
||||
OTG1_EVENT__DEVICE_CONNECTED,
|
||||
NULL);
|
||||
}
|
||||
else {
|
||||
SYNC_CLEAR_FLAG(otgc_data->otg1_device_connected,
|
||||
&otgc_data->lock);
|
||||
otgcontrol_handleInput(otgc_data,
|
||||
OTG1_EVENT__DEVICE_DISCONNECTED,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
SYNC_CLEAR_FLAG(otgc_data->one_wire_gpio_irq_is_handling,
|
||||
&otgc_data->lock);
|
||||
}
|
||||
|
||||
int otgcontrol_onewire_write_tty(struct rm_otgcontrol_data *otgc_data,
|
||||
char *device_name,
|
||||
char *text_to_send)
|
||||
{
|
||||
struct file *f;
|
||||
char buf[128];
|
||||
mm_segment_t fs;
|
||||
int i;
|
||||
|
||||
for(i = 0;i < 128;i++)
|
||||
buf[i] = 0;
|
||||
|
||||
dev_dbg(otgc_data->dev, "%s: Trying to open %s\n",
|
||||
__func__,
|
||||
device_name);
|
||||
|
||||
f = filp_open(device_name, O_RDWR, 0);
|
||||
if(f == NULL) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: filp_open error!!.\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Getting current segment descriptor\n",
|
||||
__func__);
|
||||
|
||||
fs = get_fs();
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting segment descriptor\n",
|
||||
__func__);
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Writing '%s' to file\n",
|
||||
__func__,
|
||||
text_to_send);
|
||||
|
||||
kernel_write(f,
|
||||
text_to_send,
|
||||
strlen(text_to_send),
|
||||
&f->f_pos);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Restoring segment descriptor\n",
|
||||
__func__);
|
||||
|
||||
set_fs(fs);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Closing file\n",
|
||||
__func__);
|
||||
|
||||
filp_close(f,NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int otgcontrol_onewire_read_until_cr(struct rm_otgcontrol_data *otgc_data, char *device_name, char *buf, int maxlen)
|
||||
{
|
||||
struct file *f;
|
||||
mm_segment_t fs;
|
||||
char newchar;
|
||||
int pos, state;
|
||||
|
||||
f = filp_open(device_name, O_RDONLY, 0);
|
||||
if(f == NULL) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: filp_open error!!.\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Getting current segment descriptor\n",
|
||||
__func__);
|
||||
|
||||
fs = get_fs();
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting segment descriptor\n",
|
||||
__func__);
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
pos = 0;
|
||||
state = 0;
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Starting read loop\n",
|
||||
__func__);
|
||||
do {
|
||||
kernel_read(f,
|
||||
&newchar,
|
||||
1,
|
||||
&f->f_pos);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: <-%c (0x%02x)\n",
|
||||
__func__,
|
||||
newchar, newchar);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case 0:
|
||||
if (newchar == ':') {
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: SOF\n",
|
||||
__func__);
|
||||
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (newchar != '#') {
|
||||
buf[pos++] = newchar;
|
||||
}
|
||||
}
|
||||
}while((newchar != '#') && (pos < maxlen));
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Done\n",
|
||||
__func__);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Restoring segment descriptor\n",
|
||||
__func__);
|
||||
|
||||
set_fs(fs);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Closing file\n",
|
||||
__func__);
|
||||
|
||||
filp_close(f, NULL);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Returning %d bytes read\n",
|
||||
__func__,
|
||||
pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __OTGCONTROL_ONE_WIRE_H__
|
||||
#define __OTGCONTROL_ONE_WIRE_H__
|
||||
|
||||
#include "otgcontrol.h"
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define OTG1_ONEWIRE_STATE__GPIO 0
|
||||
#define OTG1_ONEWIRE_STATE__UART_TX 1
|
||||
#define OTG1_ONEWIRE_STATE__UART_RX 2
|
||||
|
||||
#define OTG1_ONEWIRE_GPIO_STATE__DEVICE_CONNECTED 0
|
||||
#define OTG1_ONEWIRE_GPIO_STATE__DEVICE_NOT_CONNECTED 1
|
||||
|
||||
int otgcontrol_init_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
int otgcontrol_switch_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data,
|
||||
int newState);
|
||||
int otgcontrol_get_current_gpio_state(struct rm_otgcontrol_data *otgc_data);
|
||||
const char *otgcontrol_gpio_state_name(int state);
|
||||
int otgcontrol_init_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_uninit_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_activate_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_deactivate_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
static irqreturn_t otgcontrol_gpio_irq_handler(int irq, void *data);
|
||||
static void otgcontrol_gpio_irq_work(struct work_struct *work);
|
||||
int otgcontrol_onewire_read_until_cr(struct rm_otgcontrol_data *otgc_data,
|
||||
char *device_name,
|
||||
char *buf,
|
||||
int maxlen);
|
||||
int otgcontrol_onewire_write_tty(struct rm_otgcontrol_data *otgc_data,
|
||||
char *device_name,
|
||||
char *text_to_send);
|
||||
|
||||
#endif /* __OTGCONTROL_ONE_WIRE_H__ */
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "otgcontrol_sysfs.h"
|
||||
#include "otgcontrol_fsm.h"
|
||||
#include "otgcontrol_dr_mode.h"
|
||||
#include "otgcontrol_charging_ctrl.h"
|
||||
#include "otgcontrol_onewire.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
#define to_otgcontrol_data(kobj_attr_ptr, kobj_attr_member) \
|
||||
container_of(kobj_attr_ptr, struct rm_otgcontrol_data, kobj_attr_member);
|
||||
|
||||
#define SYSFS_PARENT_NODE NULL
|
||||
#define SYSFS_NODE_NAME "otgcontrol"
|
||||
|
||||
#define STATUS_GROUP_ATTRIBUTE_COUNT 1
|
||||
#define CONTROL_GROUP_ATTRIBUTE_COUNT 4
|
||||
|
||||
static struct attribute *control_attrs[CONTROL_GROUP_ATTRIBUTE_COUNT + 1] = {
|
||||
/* CONTROL_GROUP_ATTRIBUTE_COUNT number of initializing NULLs */
|
||||
NULL, NULL, NULL, NULL,
|
||||
|
||||
/* NULL terminating the list */
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group control_attr_group = {
|
||||
.attrs = control_attrs,
|
||||
.name = "control"
|
||||
};
|
||||
|
||||
struct attribute *status_attrs[STATUS_GROUP_ATTRIBUTE_COUNT + 1] = {
|
||||
/* STATUS_GROUP_ATTRIBUTE_COUNT number of NULLS */
|
||||
NULL,
|
||||
|
||||
/* NULL terminating the list */
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group status_attr_group = {
|
||||
.attrs = status_attrs,
|
||||
.name = "status"
|
||||
};
|
||||
|
||||
static ssize_t attribute_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int count, var;
|
||||
struct rm_otgcontrol_data *otgc_data;
|
||||
|
||||
if (strcmp(attr->attr.name, "otg1_device_connected") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_device_connected_attribute);
|
||||
|
||||
/* ID = 0 ==> DEVICE CONNECTED */
|
||||
var = !otgcontrol_get_current_gpio_state(otgc_data);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Returning cur otg1_device_connected value (%d)\n",
|
||||
__func__,
|
||||
var);
|
||||
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_dr_mode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_dr_mode_attribute);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Returning cur otg1_id_state value (%d)\n",
|
||||
__func__,
|
||||
otgc_data->otg1_dr_mode);
|
||||
|
||||
var = otgc_data->otg1_dr_mode;
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_chargermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_chargermode_attribute);
|
||||
|
||||
count = otgcontrol_get_otg_charger_modes(otgc_data, buf);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Returning charger mode list: %s\n",
|
||||
__func__,
|
||||
buf);
|
||||
|
||||
return count;
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_controllermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_controllermode_attribute);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Returning cur otg1_controllermode value (%d)\n",
|
||||
__func__,
|
||||
otgc_data->otg1_controllermode);
|
||||
|
||||
var = otgc_data->otg1_controllermode;
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_pinctrlstate") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_pinctrlstate_attribute);
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Returning cur pinctrlstate (%d)\n",
|
||||
__func__,
|
||||
otgc_data->otg1_pinctrlstate);
|
||||
|
||||
var = otgc_data->otg1_pinctrlstate;
|
||||
}
|
||||
else {
|
||||
pr_err("%s: Invalid attribute name (%s), returning 0\n",
|
||||
__func__,
|
||||
attr->attr.name);
|
||||
|
||||
var = 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", var);
|
||||
}
|
||||
|
||||
static ssize_t attribute_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data;
|
||||
int var, ret;
|
||||
|
||||
if (strcmp(attr->attr.name, "otg1_dr_mode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_dr_mode_attribute);
|
||||
|
||||
ret = kstrtoint(buf, 10, &var);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting new otg1 dr mode (%d)\n",
|
||||
__func__,
|
||||
var);
|
||||
|
||||
ret = otgcontrol_set_dr_mode(otgc_data,
|
||||
var);
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_chargermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_chargermode_attribute);
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting new otg1 chargermode: %s",
|
||||
__func__,
|
||||
buf);
|
||||
|
||||
ret = otgcontrol_change_otg_charger_mode_str(otgc_data, buf);
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_controllermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_controllermode_attribute);
|
||||
|
||||
ret = kstrtoint(buf, 10, &var);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting new otg1 controllermode (%d)\n",
|
||||
__func__,
|
||||
var);
|
||||
|
||||
ret = otgcontrol_handleInput(otgc_data,
|
||||
OTG1_EVENT__MODE_CHANGE_REQUESTED,
|
||||
(void*)&var);
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_pinctrlstate") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr,
|
||||
otg1_pinctrlstate_attribute);
|
||||
|
||||
ret = kstrtoint(buf, 10, &var);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(otgc_data->dev,
|
||||
"%s: Setting new pinctrlstate (%d)\n",
|
||||
__func__,
|
||||
var);
|
||||
|
||||
ret = otgcontrol_switch_one_wire_mux_state(otgc_data,
|
||||
var);
|
||||
}
|
||||
else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void otgcontrol_create_kobj_property(struct kobj_attribute *attr,
|
||||
const char *name,
|
||||
int permission,
|
||||
ssize_t (*show)(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf),
|
||||
ssize_t (*store)(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count))
|
||||
{
|
||||
attr->attr.name = name;
|
||||
attr->attr.mode = VERIFY_OCTAL_PERMISSIONS(S_IRUGO | S_IWUSR);
|
||||
attr->show = show;
|
||||
attr->store = store;
|
||||
}
|
||||
|
||||
int otgcontrol_init_sysfs_nodes(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
struct kobject *otgcontrol_kobj;
|
||||
int ret;
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_dr_mode_attribute,
|
||||
"otg1_dr_mode",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_chargermode_attribute,
|
||||
"otg1_chargermode",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_controllermode_attribute,
|
||||
"otg1_controllermode",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_pinctrlstate_attribute,
|
||||
"otg1_pinctrlstate",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
control_attrs[0] = &otgc_data->otg1_dr_mode_attribute.attr;
|
||||
control_attrs[1] = &otgc_data->otg1_chargermode_attribute.attr;
|
||||
control_attrs[2] = &otgc_data->otg1_controllermode_attribute.attr;
|
||||
control_attrs[3] = &otgc_data->otg1_pinctrlstate_attribute.attr;
|
||||
control_attrs[4] = NULL; /* NULL termination of the list */
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_device_connected_attribute,
|
||||
"otg1_device_connected",
|
||||
S_IRUGO,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
status_attrs[0] = &otgc_data->otg1_device_connected_attribute.attr;
|
||||
status_attrs[1] = NULL; /* NULL termination of the list */
|
||||
|
||||
otgcontrol_kobj = kobject_create_and_add(SYSFS_NODE_NAME,
|
||||
SYSFS_PARENT_NODE);
|
||||
if (!otgcontrol_kobj) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to create 'otgcontrol' kobject\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(otgcontrol_kobj,
|
||||
&control_attr_group);
|
||||
if (ret != 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to create 'control' attribute group\n",
|
||||
__func__);
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(otgcontrol_kobj,
|
||||
&status_attr_group);
|
||||
if (ret != 0) {
|
||||
dev_err(otgc_data->dev,
|
||||
"%s: Failed to create 'status' attribute group\n",
|
||||
__func__);
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
otgc_data->kobject = otgcontrol_kobj;
|
||||
return ret;
|
||||
|
||||
error_2:
|
||||
sysfs_remove_group(otgc_data->kobject, &control_attr_group);
|
||||
|
||||
error_1:
|
||||
kobject_put(otgc_data->kobject);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void otgcontrol_uninit_sysfs_nodes(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
if((otgc_data->kobject != NULL) && !IS_ERR(otgc_data->kobject)) {
|
||||
sysfs_remove_group(otgc_data->kobject, &control_attr_group);
|
||||
sysfs_remove_group(otgc_data->kobject, &status_attr_group);
|
||||
kobject_put(otgc_data->kobject);
|
||||
otgc_data->kobject = NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* reMarkable OTG Control
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __OTGCONTROL_SYSFS_H__
|
||||
#define __OTGCONTROL_SYSFS_H__
|
||||
|
||||
#include "otgcontrol.h"
|
||||
|
||||
int otgcontrol_init_sysfs_nodes(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_uninit_sysfs_nodes(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
#endif /* __OTGCONTROL_SYSFS_H__ */
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
|
@ -151,12 +152,30 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_pwrseq_simple_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_pwrseq_simple_resume(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SIMPLE_DEV_PM_OPS(mmc_pwrseq_simple_pm_ops, mmc_pwrseq_simple_suspend,
|
||||
mmc_pwrseq_simple_resume);
|
||||
|
||||
static struct platform_driver mmc_pwrseq_simple_driver = {
|
||||
.probe = mmc_pwrseq_simple_probe,
|
||||
.remove = mmc_pwrseq_simple_remove,
|
||||
.driver = {
|
||||
.name = "pwrseq_simple",
|
||||
.of_match_table = mmc_pwrseq_simple_of_match,
|
||||
.pm = &mmc_pwrseq_simple_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -44,6 +44,12 @@ config APM_POWER
|
|||
Say Y here to enable support APM status emulation using
|
||||
battery class devices.
|
||||
|
||||
config BD7181X_POWER
|
||||
tristate "ROHM BD71815/BD71817 Charger for Battery and Adapter Power"
|
||||
depends on MFD_BD7181X
|
||||
help
|
||||
Say Y to enable support for the BD71815/BD71817 charger.
|
||||
|
||||
config GENERIC_ADC_BATTERY
|
||||
tristate "Generic battery support using IIO"
|
||||
depends on IIO
|
||||
|
@ -386,6 +392,20 @@ config BATTERY_MAX1721X
|
|||
Say Y here to enable support for the MAX17211/MAX17215 standalone
|
||||
battery gas-gauge.
|
||||
|
||||
config BATTERY_MAX77818_UTILS
|
||||
tristate "Maxime MAX77818 Fuel Gauge common/external functions"
|
||||
help
|
||||
This is automatically selected when the MAX77818 MFD driver is
|
||||
selected, due to required Max77818 Fuel Gauge device access during
|
||||
initiation of the MFD device.
|
||||
.
|
||||
config BATTERY_MAX77818
|
||||
tristate "Maxime MAX77818 Fuel Gauge"
|
||||
depends on I2C
|
||||
depends on MFD_MAX77818
|
||||
help
|
||||
Say Y yo enable support for Maxime MAX77818 battery fuel-gauge device (part of MAX77818 multi-function reg/chrg/fg device)
|
||||
|
||||
config BATTERY_Z2
|
||||
tristate "Z2 battery driver"
|
||||
depends on I2C && MACH_ZIPIT2
|
||||
|
@ -549,6 +569,13 @@ config CHARGER_MAX8998
|
|||
Say Y to enable support for the battery charger control sysfs and
|
||||
platform data of MAX8998/LP3974 PMICs.
|
||||
|
||||
config CHARGER_MAX77818
|
||||
tristate "Maxime MAX77818 battery charger driver"
|
||||
depends on I2C
|
||||
depends on MFD_MAX77818
|
||||
help
|
||||
Say Y yo enable support for Maxime MAX77818 charger device (part of MAX77818 multi-function reg/chrg/fg device)
|
||||
|
||||
config CHARGER_QCOM_SMBB
|
||||
tristate "Qualcomm Switch-Mode Battery Charger and Boost"
|
||||
depends on MFD_SPMI_PMIC || COMPILE_TEST
|
||||
|
|
|
@ -11,6 +11,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
|
|||
|
||||
obj-$(CONFIG_PDA_POWER) += pda_power.o
|
||||
obj-$(CONFIG_APM_POWER) += apm_power.o
|
||||
obj-$(CONFIG_BD7181X_POWER) += bd7181x-power.o
|
||||
obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o
|
||||
obj-$(CONFIG_MAX8925_POWER) += max8925_power.o
|
||||
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o
|
||||
|
@ -51,6 +52,7 @@ obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o
|
|||
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX77818) += max77818_battery.o
|
||||
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
|
||||
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
|
||||
obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o
|
||||
|
@ -76,6 +78,7 @@ obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o
|
|||
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77818) += max77818-charger.o
|
||||
obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o
|
||||
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
|
||||
|
@ -93,3 +96,4 @@ obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
|
|||
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
|
||||
obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o
|
||||
obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o
|
||||
obj-$(CONFIG_BATTERY_MAX77818_UTILS) += max77818_battery_utils.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Maxim MAX77818 Battery Utils
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
* Author: Shawn Guo <shawn.guo@linaro.org>
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/power/max77818_battery_utils.h>
|
||||
#include <linux/power/max17042_battery.h>
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Config register bits */
|
||||
#define CONFIG_FGCC_BIT (1 << 11)
|
||||
|
||||
/* Parameter to be given from command line in order to tune the delay introduced after
|
||||
* clearing the FGCC bit before forwarding requests to the charger driver */
|
||||
static int post_fgcc_change_delay_us = 100000;
|
||||
|
||||
/* DO NOT CALL DIRECTLY !!
|
||||
*
|
||||
* ONLY TO _BE CALLED FROM MAX77818_DO_NON_FGCC_OP macro */
|
||||
int max77818_utils_set_fgcc_mode(struct max77818_dev *max77818_dev,
|
||||
bool enabled,
|
||||
bool *cur_mode)
|
||||
{
|
||||
unsigned int read_data;
|
||||
int ret;
|
||||
|
||||
if (cur_mode) {
|
||||
ret = regmap_read(max77818_dev->regmap_fg,
|
||||
MAX17042_CONFIG, &read_data);
|
||||
if (ret) {
|
||||
dev_err(max77818_dev->dev,
|
||||
"Failed to read CONFIG register\n");
|
||||
return ret;
|
||||
}
|
||||
*cur_mode = (read_data & CONFIG_FGCC_BIT);
|
||||
}
|
||||
|
||||
dev_dbg(max77818_dev->dev, "Turning %s FGCC\n", enabled ? "on" : "off");
|
||||
ret = regmap_update_bits(max77818_dev->regmap_fg,
|
||||
MAX17042_CONFIG,
|
||||
CONFIG_FGCC_BIT,
|
||||
enabled ? CONFIG_FGCC_BIT : 0x0000);
|
||||
|
||||
if (ret) {
|
||||
dev_err(max77818_dev->dev,
|
||||
"Failed to %s FGCC bit in CONFIG register\n",
|
||||
enabled ? "set" : "clear");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(max77818_dev->dev,
|
||||
"Waiting %d us after FGCC mode change..\n",
|
||||
post_fgcc_change_delay_us);
|
||||
usleep_range(post_fgcc_change_delay_us, post_fgcc_change_delay_us + 100000);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -77,6 +77,15 @@ static const char * const power_supply_scope_text[] = {
|
|||
"Unknown", "System", "Device"
|
||||
};
|
||||
|
||||
static const char * const power_supply_charger_mode_text[] = {
|
||||
"Charger", "OTG Supply", "Off"
|
||||
};
|
||||
|
||||
static const char * const power_supply_status_ex_text[] = {
|
||||
"Charger not connected", "POGO connected", "USB-C connected",
|
||||
"POGO/USB-C connected", "Changing", "Unknown"
|
||||
};
|
||||
|
||||
static ssize_t power_supply_show_usb_type(struct device *dev,
|
||||
enum power_supply_usb_type *usb_types,
|
||||
ssize_t num_usb_types,
|
||||
|
@ -171,6 +180,14 @@ static ssize_t power_supply_show_property(struct device *dev,
|
|||
ret = sprintf(buf, "%s\n",
|
||||
power_supply_scope_text[value.intval]);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGER_MODE:
|
||||
ret = sprintf(buf, "%s\n",
|
||||
power_supply_charger_mode_text[value.intval]);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS_EX:
|
||||
ret = sprintf(buf, "%s\n",
|
||||
power_supply_status_ex_text[value.intval]);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
||||
ret = sprintf(buf, "%s\n", value.strval);
|
||||
break;
|
||||
|
@ -208,6 +225,12 @@ static ssize_t power_supply_store_property(struct device *dev,
|
|||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
ret = sysfs_match_string(power_supply_scope_text, buf);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGER_MODE:
|
||||
ret = sysfs_match_string(power_supply_charger_mode_text, buf);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS_EX:
|
||||
ret = sysfs_match_string(power_supply_status_ex_text, buf);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
@ -255,6 +278,7 @@ static struct device_attribute power_supply_attrs[] = {
|
|||
POWER_SUPPLY_ATTR(voltage_ocv),
|
||||
POWER_SUPPLY_ATTR(voltage_boot),
|
||||
POWER_SUPPLY_ATTR(current_max),
|
||||
POWER_SUPPLY_ATTR(current_max2),
|
||||
POWER_SUPPLY_ATTR(current_now),
|
||||
POWER_SUPPLY_ATTR(current_avg),
|
||||
POWER_SUPPLY_ATTR(current_boot),
|
||||
|
@ -310,6 +334,16 @@ static struct device_attribute power_supply_attrs[] = {
|
|||
POWER_SUPPLY_ATTR(model_name),
|
||||
POWER_SUPPLY_ATTR(manufacturer),
|
||||
POWER_SUPPLY_ATTR(serial_number),
|
||||
|
||||
/* MAX77818-charger specific property to switch between OTG Supply (host mode)
|
||||
* and charging (device mode)
|
||||
*/
|
||||
POWER_SUPPLY_ATTR(charger_mode),
|
||||
|
||||
/* MAX77818-charger specific property to get extended charger status indicating
|
||||
* which of the two charger inputs are connected
|
||||
*/
|
||||
POWER_SUPPLY_ATTR(status_ex),
|
||||
};
|
||||
|
||||
static struct attribute *
|
||||
|
|
|
@ -204,6 +204,11 @@ config REGULATOR_BD718XX
|
|||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called bd718x7-regulator.
|
||||
config REGULATOR_BD7181X
|
||||
tristate "RoHM BD71815/BD71817 Power Regulator"
|
||||
depends on MFD_BD7181X
|
||||
help
|
||||
This driver supports BD71815/BD71817 voltage regulator chips.
|
||||
|
||||
config REGULATOR_BD9571MWV
|
||||
tristate "ROHM BD9571MWV Regulators"
|
||||
|
@ -575,6 +580,13 @@ config REGULATOR_MAX77802
|
|||
Exynos5420/Exynos5800 SoCs to control various voltages.
|
||||
It includes support for control of voltage and ramp speed.
|
||||
|
||||
config REGULATOR_MAX77818
|
||||
tristate "Maxim MAX77818 voltage regulator"
|
||||
depends on MFD_MAX77818
|
||||
depends on I2C
|
||||
help
|
||||
Say y here to support the regulator found in the Maxime Semiconductor MAX77818 multi-function device
|
||||
|
||||
config REGULATOR_MC13XXX_CORE
|
||||
tristate
|
||||
|
||||
|
@ -878,6 +890,12 @@ config REGULATOR_STM32_BOOSTER
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called stm32-booster.
|
||||
|
||||
config REGULATOR_SY7636A
|
||||
tristate "Silergy SY7636A voltage regulator"
|
||||
depends on MFD_SY7636A
|
||||
help
|
||||
This driver supports Silergy SY3686A voltage regulator.
|
||||
|
||||
config REGULATOR_STM32_VREFBUF
|
||||
tristate "STMicroelectronics STM32 VREFBUF"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
|
|
|
@ -29,6 +29,7 @@ obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD7181X) += bd7181x-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
|
||||
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
|
||||
|
@ -74,6 +75,7 @@ obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
|
|||
obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MAX77818) += max77818-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
|
||||
|
@ -110,6 +112,7 @@ obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o
|
||||
obj-$(CONFIG_REGULATOR_SY7636A) += sy7636a-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
|
||||
obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
|
||||
obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
|
||||
|
|
|
@ -0,0 +1,788 @@
|
|||
/*
|
||||
* @file bd7181x-regulator.c RoHM BD71815/BD71817 regulator driver
|
||||
*
|
||||
* Copyright 2014 Embest Technology Co. Ltd. Inc.
|
||||
*
|
||||
* @author Tony Luo <luofc@embedinfo.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/bd7181x.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
|
||||
#define BD7181X_VOL_OFFSET 0
|
||||
#define BD7181X_STANDBY_OFFSET 0
|
||||
#define BD7181X_DVS_BUCK_NUM 2
|
||||
#define BD7181X_DVS_HIGH_LOW 2
|
||||
|
||||
struct bd7181x_buck_dvs {
|
||||
int i2c_dvs_enable;
|
||||
u32 voltage[BD7181X_DVS_HIGH_LOW];
|
||||
};
|
||||
|
||||
struct bd7181x_regulator {
|
||||
struct regulator_desc desc;
|
||||
unsigned char stby_reg;
|
||||
unsigned char stby_mask;
|
||||
};
|
||||
|
||||
/** @brief bd7181x regulator type */
|
||||
struct bd7181x_pmic {
|
||||
struct bd7181x_regulator descs[BD7181X_REGULATOR_CNT]; /**< regulator description to system */
|
||||
struct bd7181x *mfd; /**< parent device */
|
||||
struct device *dev; /**< regulator kernel device */
|
||||
struct regulator_dev *rdev[BD7181X_REGULATOR_CNT]; /**< regulator device of system */
|
||||
struct bd7181x_buck_dvs buck_dvs[BD7181X_DVS_BUCK_NUM]; /**< buck1/2 dvs */
|
||||
};
|
||||
|
||||
static const int bd7181x_wled_currents[] = {
|
||||
// 0x00
|
||||
10, 20, 30, 50,
|
||||
70, 100, 200, 300,
|
||||
500, 700, 1000, 2000,
|
||||
3000, 4000, 5000, 6000,
|
||||
// 0x10
|
||||
7000, 8000, 9000, 10000,
|
||||
11000, 12000, 13000, 14000,
|
||||
15000, 16000, 17000, 18000,
|
||||
19000, 20000, 21000, 22000,
|
||||
// 0x20
|
||||
23000, 24000, 25000,
|
||||
};
|
||||
|
||||
/*
|
||||
* BUCK1/2
|
||||
* BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
|
||||
* 00: 10.00mV/usec 10mV 1uS
|
||||
* 01: 5.00mV/usec 10mV 2uS
|
||||
* 10: 2.50mV/usec 10mV 4uS
|
||||
* 11: 1.25mV/usec 10mV 8uS
|
||||
*/
|
||||
static int bd7181x_buck12_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev);
|
||||
struct bd7181x *mfd = pmic->mfd;
|
||||
int id = rdev->desc->id;
|
||||
unsigned int ramp_value = BUCK1_RAMPRATE_10P00MV;
|
||||
|
||||
switch (ramp_delay) {
|
||||
case 1 ... 1250:
|
||||
ramp_value = BUCK1_RAMPRATE_1P25MV;
|
||||
break;
|
||||
case 1251 ... 2500:
|
||||
ramp_value = BUCK1_RAMPRATE_2P50MV;
|
||||
break;
|
||||
case 2501 ... 5000:
|
||||
ramp_value = BUCK1_RAMPRATE_5P00MV;
|
||||
break;
|
||||
case 5001 ... 10000:
|
||||
ramp_value = BUCK1_RAMPRATE_10P00MV;
|
||||
break;
|
||||
default:
|
||||
ramp_value = BUCK1_RAMPRATE_10P00MV;
|
||||
dev_err(pmic->dev, "%s: ramp_delay: %d not supported, setting 10000mV//us\n",
|
||||
rdev->desc->name, ramp_delay);
|
||||
}
|
||||
|
||||
return regmap_update_bits(mfd->regmap, BD7181X_REG_BUCK1_MODE + id*0x1,
|
||||
BUCK1_RAMPRATE_MASK, ramp_value << 6);
|
||||
}
|
||||
|
||||
static int bd7181x_led_set_current_limit(struct regulator_dev *rdev,
|
||||
int min_uA, int max_uA)
|
||||
{
|
||||
struct bd7181x_pmic* pmic = rdev_get_drvdata(rdev);
|
||||
struct bd7181x* mfd = pmic->mfd;
|
||||
u8 addr;
|
||||
// int id = rdev_get_id(rdev);
|
||||
int i;
|
||||
|
||||
addr = BD7181X_REG_LED_DIMM;
|
||||
|
||||
for (i = ARRAY_SIZE(bd7181x_wled_currents) - 1 ; i >= 0; i--) {
|
||||
if (bd7181x_wled_currents[i] >= min_uA &&
|
||||
bd7181x_wled_currents[i] <= max_uA)
|
||||
return bd7181x_update_bits(mfd, addr, 0x3F, i);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bd7181x_led_get_current_limit(struct regulator_dev *rdev)
|
||||
{
|
||||
struct bd7181x_pmic* pmic = rdev_get_drvdata(rdev);
|
||||
struct bd7181x* mfd = pmic->mfd;
|
||||
// int id = rdev_get_id(rdev);
|
||||
u8 addr;
|
||||
int r;
|
||||
|
||||
addr = BD7181X_REG_LED_DIMM;
|
||||
|
||||
r = bd7181x_reg_read(mfd, addr);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = r & 0x3F;
|
||||
|
||||
return (r < ARRAY_SIZE(bd7181x_wled_currents)) ?
|
||||
bd7181x_wled_currents[r] : -EINVAL;
|
||||
}
|
||||
|
||||
static int bd7181x_buck12_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev);
|
||||
int rid = rdev_get_id(rdev);
|
||||
struct bd7181x *bd7181x = pmic->mfd;
|
||||
int ret, val;
|
||||
u8 regh = BD7181X_REG_BUCK1_VOLT_H + rid*0x2,
|
||||
regl = BD7181X_REG_BUCK1_VOLT_L + rid*0x2;
|
||||
|
||||
ret = bd7181x_reg_read(bd7181x, regh);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
val = ret;
|
||||
if((!(val & BUCK1_STBY_DVS)) && (!(val & BUCK1_DVSSEL))) {
|
||||
ret = bd7181x_reg_read(bd7181x, regl);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
val = ret & BUCK1_L_MASK;
|
||||
} else {
|
||||
val &= BUCK1_H_MASK;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* For Buck 1/2.
|
||||
*
|
||||
*/
|
||||
static int bd7181x_buck12_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev);
|
||||
int rid = rdev_get_id(rdev);
|
||||
struct bd7181x *bd7181x = pmic->mfd;
|
||||
int ret, val;
|
||||
u8 regh = BD7181X_REG_BUCK1_VOLT_H + rid*0x2,
|
||||
regl = BD7181X_REG_BUCK1_VOLT_L + rid*0x2;
|
||||
|
||||
ret = bd7181x_reg_read(bd7181x, regh);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
val = ret;
|
||||
if((!(val & BUCK1_STBY_DVS)) && (!(val & BUCK1_DVSSEL))) {
|
||||
ret = bd7181x_reg_write(bd7181x, regl, sel & BUCK1_L_MASK);
|
||||
} else {
|
||||
val = (val & 0xC0) | (sel & BUCK1_H_MASK);
|
||||
ret = bd7181x_reg_write(bd7181x, regh, val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct regulator_ops bd7181x_ldo_regulator_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
static struct regulator_ops bd7181x_fixed_regulator_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
};
|
||||
|
||||
static struct regulator_ops bd7181x_buck_regulator_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
};
|
||||
|
||||
static struct regulator_ops bd7181x_buck12_regulator_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.set_voltage_sel = bd7181x_buck12_set_voltage_sel,
|
||||
.get_voltage_sel = bd7181x_buck12_get_voltage_sel,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
.set_ramp_delay = bd7181x_buck12_set_ramp_delay,
|
||||
};
|
||||
|
||||
static struct regulator_ops bd7181x_led_regulator_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_ascend,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_current_limit = bd7181x_led_set_current_limit,
|
||||
.get_current_limit = bd7181x_led_get_current_limit,
|
||||
};
|
||||
|
||||
#define BD7181X_FIXED_REG(_name, ereg, emsk, voltage) \
|
||||
[BD7181X_ ## _name] = { \
|
||||
.desc = { \
|
||||
.name = #_name, \
|
||||
.n_voltages = 1, \
|
||||
.ops = &bd7181x_fixed_regulator_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = BD7181X_ ## _name, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (voltage), \
|
||||
.enable_reg = (ereg), \
|
||||
.enable_mask = (emsk), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define BD7181X_BUCK_REG(_name, base, ereg, min, max, step) \
|
||||
[BD7181X_ ## _name] = { \
|
||||
.desc = { \
|
||||
.name = #_name,\
|
||||
.n_voltages = ((max) - (min)) / (step) + 1, \
|
||||
.ops = &bd7181x_buck_regulator_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = BD7181X_ ## _name, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (min), \
|
||||
.uV_step = (step), \
|
||||
.vsel_reg = (base) + BD7181X_VOL_OFFSET, \
|
||||
.vsel_mask = 0x3f, \
|
||||
.enable_reg = (ereg), \
|
||||
.enable_mask = 0x04, \
|
||||
}, \
|
||||
.stby_reg = (base) + BD7181X_STANDBY_OFFSET, \
|
||||
.stby_mask = 0x3f, \
|
||||
}
|
||||
|
||||
#define BD7181X_BUCK12_REG(_name, base, ereg, min, max, step) \
|
||||
[BD7181X_ ## _name] = { \
|
||||
.desc = { \
|
||||
.name = #_name,\
|
||||
.n_voltages = ((max) - (min)) / (step) + 1, \
|
||||
.ops = &bd7181x_buck12_regulator_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = BD7181X_ ## _name, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (min), \
|
||||
.uV_step = (step), \
|
||||
.vsel_reg = (base) + BD7181X_VOL_OFFSET, \
|
||||
.vsel_mask = 0x3f, \
|
||||
.enable_reg = (ereg), \
|
||||
.enable_mask = 0x04, \
|
||||
}, \
|
||||
.stby_reg = (base) + BD7181X_STANDBY_OFFSET, \
|
||||
.stby_mask = 0x3f, \
|
||||
}
|
||||
|
||||
#define BD7181X_LED_REG(_name, base, mask, ereg, emsk, voltages) \
|
||||
[BD7181X_ ## _name] = { \
|
||||
.desc = { \
|
||||
.name = #_name, \
|
||||
.n_voltages = ARRAY_SIZE(voltages), \
|
||||
.ops = &bd7181x_led_regulator_ops, \
|
||||
.type = REGULATOR_CURRENT, \
|
||||
.id = BD7181X_ ## _name, \
|
||||
.owner = THIS_MODULE, \
|
||||
.volt_table = voltages, \
|
||||
.vsel_reg = (base), \
|
||||
.vsel_mask = (mask), \
|
||||
.enable_reg = (ereg), \
|
||||
.enable_mask = (emsk), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define BD7181X_LDO_REG(_name, base, ereg, emsk, min, max, step) \
|
||||
[BD7181X_ ## _name] = { \
|
||||
.desc = { \
|
||||
.name = #_name, \
|
||||
.n_voltages = ((max) - (min)) / (step) + 1, \
|
||||
.ops = &bd7181x_ldo_regulator_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = BD7181X_ ## _name, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (min), \
|
||||
.uV_step = (step), \
|
||||
.vsel_reg = (base), \
|
||||
.vsel_mask = 0x3f, \
|
||||
.enable_reg = (ereg), \
|
||||
.enable_mask = (emsk), \
|
||||
}, \
|
||||
.stby_reg = (base), \
|
||||
.stby_mask = 0x20, \
|
||||
}
|
||||
|
||||
static struct bd7181x_regulator bd7181x_regulators[] = {
|
||||
BD7181X_BUCK12_REG(BUCK1, BD7181X_REG_BUCK1_VOLT_H, BD7181X_REG_BUCK1_MODE, 800000, 2000000, 25000),
|
||||
BD7181X_BUCK12_REG(BUCK2, BD7181X_REG_BUCK2_VOLT_H, BD7181X_REG_BUCK2_MODE, 800000, 2000000, 25000),
|
||||
BD7181X_BUCK_REG(BUCK3, BD7181X_REG_BUCK3_VOLT, BD7181X_REG_BUCK3_MODE, 1200000, 2700000, 50000),
|
||||
BD7181X_BUCK_REG(BUCK4, BD7181X_REG_BUCK4_VOLT, BD7181X_REG_BUCK4_MODE, 1100000, 1850000, 25000),
|
||||
BD7181X_BUCK_REG(BUCK5, BD7181X_REG_BUCK5_VOLT, BD7181X_REG_BUCK5_MODE, 1800000, 3300000, 50000),
|
||||
BD7181X_LDO_REG(LDO1, BD7181X_REG_LDO1_VOLT, BD7181X_REG_LDO_MODE1, 0x40, 800000, 3300000, 50000),
|
||||
BD7181X_LDO_REG(LDO2, BD7181X_REG_LDO2_VOLT, BD7181X_REG_LDO_MODE2, 0x04, 800000, 3300000, 50000),
|
||||
BD7181X_LDO_REG(LDO3, BD7181X_REG_LDO3_VOLT, BD7181X_REG_LDO_MODE2, 0x40, 800000, 3300000, 50000),
|
||||
BD7181X_LDO_REG(LDO4, BD7181X_REG_LDO4_VOLT, BD7181X_REG_LDO_MODE3, 0x04, 800000, 3300000, 50000),
|
||||
BD7181X_LDO_REG(LDO5, BD7181X_REG_LDO5_VOLT_H,BD7181X_REG_LDO_MODE3,0x40, 800000, 3300000, 50000),
|
||||
BD7181X_FIXED_REG(LDODVREF, BD7181X_REG_LDO_MODE4, 0x40, 3000000),
|
||||
BD7181X_FIXED_REG(LDOLPSR, BD7181X_REG_LDO_MODE4, 0x04, 1800000),
|
||||
BD7181X_LED_REG(WLED, BD7181X_REG_LED_DIMM, 0x3F, BD7181X_REG_LED_CTRL, 0x04, bd7181x_wled_currents),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static struct of_regulator_match bd7181x_matches[] = {
|
||||
{ .name = "buck1", },
|
||||
{ .name = "buck2", },
|
||||
{ .name = "buck3", },
|
||||
{ .name = "buck4", },
|
||||
{ .name = "buck5", },
|
||||
{ .name = "ldo1", },
|
||||
{ .name = "ldo2", },
|
||||
{ .name = "ldo3", },
|
||||
{ .name = "ldo4", },
|
||||
{ .name = "ldo5", },
|
||||
{ .name = "dvref", },
|
||||
{ .name = "lpsr", },
|
||||
{ .name = "wled", },
|
||||
};
|
||||
|
||||
/**@brief parse bd7181x regulator device tree
|
||||
* @param pdev platform device of bd7181x regulator
|
||||
* @param bd7181x_reg_matches return regualtor matches
|
||||
* @retval 0 parse success
|
||||
* @retval NULL parse fail
|
||||
*/
|
||||
static int bd7181x_parse_dt_reg_data(
|
||||
struct platform_device *pdev,
|
||||
struct of_regulator_match **reg_matches)
|
||||
{
|
||||
// struct bd7181x *bd7181x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device_node *np, *regulators;
|
||||
struct of_regulator_match *matches;
|
||||
int ret, count;
|
||||
|
||||
np = of_node_get(pdev->dev.parent->of_node);
|
||||
regulators = of_find_node_by_name(np, "regulators");
|
||||
if (!regulators) {
|
||||
dev_err(&pdev->dev, "regulator node not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
count = ARRAY_SIZE(bd7181x_matches);
|
||||
matches = bd7181x_matches;
|
||||
|
||||
ret = of_regulator_match(&pdev->dev, regulators, matches, count);
|
||||
of_node_put(regulators);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*reg_matches = matches;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int bd7181x_parse_dt_reg_data(
|
||||
struct platform_device *pdev,
|
||||
struct of_regulator_match **reg_matches)
|
||||
{
|
||||
*reg_matches = NULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @brief out32k mode constants */
|
||||
static const char* out32k_modes[] = {"open_drain", "cmos"};
|
||||
|
||||
/** @brief retrive out32k output mode */
|
||||
static ssize_t show_mode(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
|
||||
int o;
|
||||
|
||||
o = bd7181x_reg_read(pmic->mfd, BD7181X_REG_OUT32K);
|
||||
o = (o & OUT32K_MODE) != 0;
|
||||
|
||||
return sprintf(buf, "%s\n", out32k_modes[o]);
|
||||
}
|
||||
|
||||
/** @brief set out32k output mode */
|
||||
static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
|
||||
int o, r;
|
||||
|
||||
if (strncmp(buf, out32k_modes[0], strlen(out32k_modes[0])) == 0) {
|
||||
o = 0;
|
||||
} else {
|
||||
o = OUT32K_MODE;
|
||||
}
|
||||
|
||||
r = bd7181x_update_bits(pmic->mfd, BD7181X_REG_OUT32K, OUT32K_MODE, o);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** @brief retrive out32k output value */
|
||||
static ssize_t show_value(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
|
||||
int o;
|
||||
|
||||
o = bd7181x_reg_read(pmic->mfd, BD7181X_REG_OUT32K);
|
||||
o = (o & OUT32K_EN) != 0;
|
||||
|
||||
return sprintf(buf, "%d\n", o);
|
||||
}
|
||||
|
||||
/** @brief set o output value */
|
||||
static ssize_t set_value(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
|
||||
int o, r;
|
||||
|
||||
if (sscanf(buf, "%d", &o) < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (o != 0) {
|
||||
o = OUT32K_EN;
|
||||
}
|
||||
r = bd7181x_update_bits(pmic->mfd, BD7181X_REG_OUT32K, OUT32K_EN, o);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** @brief list all supported modes */
|
||||
static ssize_t available_modes(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
r = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(out32k_modes) && r >= 0; i++) {
|
||||
r += sprintf(buf + r, "%s ", out32k_modes[i]);
|
||||
}
|
||||
r += sprintf(buf + r, "\n");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/** @brief list all supported values */
|
||||
static ssize_t available_values(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "0 1 \n");
|
||||
}
|
||||
|
||||
|
||||
/** @brief retrive dvssel output value */
|
||||
static ssize_t show_dvssel(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
|
||||
int value, index = 0, i;
|
||||
|
||||
for (i = 0; i < BD7181X_DVS_BUCK_NUM; i++) {
|
||||
value = bd7181x_reg_read(pmic->mfd, BD7181X_REG_BUCK1_VOLT_H + i*0x2);
|
||||
if(value < 0)
|
||||
return value;
|
||||
value = (value & BUCK1_DVSSEL) != 0;
|
||||
index += sprintf(buf+index, "BUCK%i: %d\n", i, value);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/** @brief set o output value */
|
||||
static ssize_t set_dvssel(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
|
||||
int devsel1, devsel2, ret;
|
||||
|
||||
if (sscanf(buf, "%d %d", &devsel1, &devsel2) < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = bd7181x_update_bits(pmic->mfd, BD7181X_REG_BUCK1_VOLT_H, BUCK1_DVSSEL, devsel1<<7);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = bd7181x_update_bits(pmic->mfd, BD7181X_REG_BUCK2_VOLT_H, BUCK2_DVSSEL, devsel2<<7);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(out32k_mode, S_IWUSR | S_IRUGO, show_mode, set_mode);
|
||||
static DEVICE_ATTR(out32k_value, S_IWUSR | S_IRUGO, show_value, set_value);
|
||||
static DEVICE_ATTR(available_mode, S_IWUSR | S_IRUGO, available_modes, NULL);
|
||||
static DEVICE_ATTR(available_value, S_IWUSR | S_IRUGO, available_values, NULL);
|
||||
static DEVICE_ATTR(dvssel, S_IWUSR | S_IRUGO, show_dvssel, set_dvssel);
|
||||
|
||||
/** @brief device sysfs attribute table, about o */
|
||||
static struct attribute *gpo_attributes[] = {
|
||||
&dev_attr_out32k_mode.attr,
|
||||
&dev_attr_out32k_value.attr,
|
||||
&dev_attr_available_mode.attr,
|
||||
&dev_attr_available_value.attr,
|
||||
&dev_attr_dvssel.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group gpo_attr_group = {
|
||||
.attrs = gpo_attributes,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
#ifdef CONFIG_OF
|
||||
/** @brief buck1/2 dvs enable/voltage from device tree
|
||||
* @param pdev platfrom device pointer
|
||||
* @param buck_dvs pointer
|
||||
* @return void
|
||||
*/
|
||||
static void of_bd7181x_buck_dvs(struct platform_device *pdev, struct bd7181x_buck_dvs *buck_dvs)
|
||||
{
|
||||
struct device_node *pmic_np;
|
||||
|
||||
pmic_np = of_node_get(pdev->dev.parent->of_node);
|
||||
if (!pmic_np) {
|
||||
dev_err(&pdev->dev, "could not find pmic sub-node\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_get_property(pmic_np, "bd7181x,pmic-buck1-uses-i2c-dvs", NULL)) {
|
||||
buck_dvs[0].i2c_dvs_enable = 1;
|
||||
if (of_property_read_u32_array(pmic_np,
|
||||
"bd7181x,pmic-buck1-dvs-voltage",
|
||||
&buck_dvs[0].voltage[0], 2)) {
|
||||
dev_err(&pdev->dev, "buck1 voltages not specified\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (of_get_property(pmic_np, "bd7181x,pmic-buck2-uses-i2c-dvs", NULL)) {
|
||||
buck_dvs[1].i2c_dvs_enable = 1;
|
||||
if (of_property_read_u32_array(pmic_np,
|
||||
"bd7181x,pmic-buck2-dvs-voltage",
|
||||
&buck_dvs[1].voltage[0], 2)) {
|
||||
dev_err(&pdev->dev, "buck2 voltages not specified\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void of_bd7181x_buck_dvs(struct platform_device *pdev, struct bd7181x_buck_dvs *buck_dvs)
|
||||
{
|
||||
buck_dvs[0].i2c_dvs_enable = 0;
|
||||
buck_dvs[0].voltage[0] = BUCK1_H_DEFAULT;
|
||||
buck_dvs[0].voltage[1] = BUCK1_L_DEFAULT;
|
||||
buck_dvs[1].i2c_dvs_enable = 0;
|
||||
buck_dvs[1].voltage[0] = BUCK1_H_DEFAULT;
|
||||
buck_dvs[1].voltage[1] = BUCK1_L_DEFAULT;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int bd7181x_buck12_dvs_init(struct bd7181x_pmic *pmic)
|
||||
{
|
||||
struct bd7181x *bd7181x = pmic->mfd;
|
||||
struct bd7181x_buck_dvs *buck_dvs = &pmic->buck_dvs[0];
|
||||
int i, ret, val, selector = 0;
|
||||
u8 regh, regl;
|
||||
|
||||
for(i = 0; i < BD7181X_DVS_BUCK_NUM; i++, buck_dvs++) {
|
||||
if (!buck_dvs->i2c_dvs_enable)
|
||||
continue;
|
||||
|
||||
regh = BD7181X_REG_BUCK1_VOLT_H + i*0x2;
|
||||
regl = BD7181X_REG_BUCK1_VOLT_L + i*0x2;
|
||||
val = BUCK1_DVSSEL;
|
||||
dev_info(pmic->dev, "Buck%d: I2C DVS Enabled !\n", i);
|
||||
val &= ~BUCK1_STBY_DVS;
|
||||
dev_info(pmic->dev, "Buck%d: DVS High-Low[%d - %d].\n", i, buck_dvs->voltage[0], buck_dvs->voltage[1]);
|
||||
selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[0], buck_dvs->voltage[0]);
|
||||
if(selector < 0) {
|
||||
dev_err(pmic->dev, "%s(): not found selector for voltage [%d]\n", __func__, buck_dvs->voltage[0]);
|
||||
} else {
|
||||
ret = bd7181x_reg_write(bd7181x, regh, val | (selector & BUCK1_H_MASK));
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[1], buck_dvs->voltage[1]);
|
||||
if(selector < 0) {
|
||||
dev_err(pmic->dev, "%s(): not found selector for voltage [%d]\n", __func__, buck_dvs->voltage[1]);
|
||||
} else {
|
||||
ret = bd7181x_reg_write(bd7181x, regl, val | (selector & BUCK1_L_MASK));
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**@brief probe bd7181x regulator device
|
||||
@param pdev bd7181x regulator platform device
|
||||
@retval 0 success
|
||||
@retval negative fail
|
||||
*/
|
||||
static int bd7181x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bd7181x_pmic *pmic;
|
||||
struct bd7181x_board *pdata;
|
||||
struct regulator_config config = {};
|
||||
struct bd7181x *bd7181x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct of_regulator_match *matches = NULL;
|
||||
int i, err;
|
||||
|
||||
pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
|
||||
if (!pmic) {
|
||||
dev_err(&pdev->dev, "Memory allocation failed for pmic\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(pmic->descs, bd7181x_regulators, sizeof(pmic->descs));
|
||||
|
||||
pmic->dev = &pdev->dev;
|
||||
pmic->mfd = bd7181x;
|
||||
platform_set_drvdata(pdev, pmic);
|
||||
|
||||
pdata = dev_get_platdata(bd7181x->dev);
|
||||
if (!pdata && bd7181x->dev->of_node) {
|
||||
bd7181x_parse_dt_reg_data(pdev, &matches);
|
||||
if (matches == NULL) {
|
||||
dev_err(&pdev->dev, "Platform data not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get buck dvs parameters */
|
||||
of_bd7181x_buck_dvs(pdev, &pmic->buck_dvs[0]);
|
||||
|
||||
for (i = 0; i < BD7181X_REGULATOR_CNT; i++) {
|
||||
struct regulator_init_data *init_data;
|
||||
struct regulator_desc *desc;
|
||||
struct regulator_dev *rdev;
|
||||
|
||||
desc = &pmic->descs[i].desc;
|
||||
desc->name = bd7181x_matches[i].name;
|
||||
|
||||
if (pdata) {
|
||||
init_data = pdata->init_data[i];
|
||||
} else {
|
||||
init_data = matches[i].init_data;
|
||||
}
|
||||
|
||||
config.dev = pmic->dev;
|
||||
config.init_data = init_data;
|
||||
config.driver_data = pmic;
|
||||
config.regmap = bd7181x->regmap;
|
||||
config.of_node = matches[i].of_node;
|
||||
|
||||
rdev = regulator_register(desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(bd7181x->dev,
|
||||
"failed to register %s regulator\n",
|
||||
desc->name);
|
||||
err = PTR_ERR(rdev);
|
||||
goto err;
|
||||
}
|
||||
pmic->rdev[i] = rdev;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &gpo_attr_group);
|
||||
if (err != 0) {
|
||||
dev_err(&pdev->dev, "Failed to create attribute group: %d\n", err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Init buck12 dvs */
|
||||
err = bd7181x_buck12_dvs_init(pmic);
|
||||
if (err != 0) {
|
||||
dev_err(&pdev->dev, "Failed to buck12 dvs: %d\n", err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
while (--i >= 0)
|
||||
regulator_unregister(pmic->rdev[i]);
|
||||
|
||||
kfree(pmic);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**@brief remove bd7181x regulator device
|
||||
@param pdev bd7181x regulator platform device
|
||||
@return 0
|
||||
*/
|
||||
static int bd7181x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bd7181x_pmic *pmic = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &gpo_attr_group);
|
||||
|
||||
for (i = 0; i < BD7181X_REGULATOR_CNT; i++)
|
||||
regulator_unregister(pmic->rdev[i]);
|
||||
|
||||
kfree(pmic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bd7181x_driver = {
|
||||
.driver = {
|
||||
.name = "bd7181x-pmic",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = bd7181x_probe,
|
||||
.remove = bd7181x_remove,
|
||||
};
|
||||
|
||||
/**@brief module initialize function */
|
||||
static int __init bd7181x_init(void)
|
||||
{
|
||||
return platform_driver_register(&bd7181x_driver);
|
||||
}
|
||||
subsys_initcall(bd7181x_init);
|
||||
|
||||
/**@brief module deinitialize function */
|
||||
static void __exit bd7181x_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&bd7181x_driver);
|
||||
}
|
||||
module_exit(bd7181x_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Tony Luo <luofc@embedinfo.com>");
|
||||
MODULE_DESCRIPTION("BD71815/BD71817 voltage regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:bd7181x-pmic");
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. 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 and
|
||||
* only 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 driver is based on max77823-regulator.c
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/max77818/max77818.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define REG_SAFEOUTCTRL 0xC6
|
||||
#define BIT_SAFEOUT1 GENMASK(1, 0)
|
||||
#define BIT_SAFEOUT2 GENMASK(3, 2)
|
||||
#define BIT_ACTDISSAFEO1 BIT(4)
|
||||
#define BIT_ACTDISSAFEO2 BIT(5)
|
||||
#define BIT_ENSAFEOUT1 BIT(6)
|
||||
#define BIT_ENSAFEOUT2 BIT(7)
|
||||
|
||||
/* MAX77818 regulator IDs */
|
||||
enum max77818_regulators {
|
||||
MAX77818_SAFEOUT1 = 0,
|
||||
MAX77818_SAFEOUT2,
|
||||
};
|
||||
|
||||
const static unsigned int max77818_safeout_volt_table[] =
|
||||
{
|
||||
4850000, 4900000, 4950000, 3300000,
|
||||
};
|
||||
|
||||
static struct regulator_ops max77818_safeout_ops = {
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_ascend,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
#define REGULATOR_DESC_SFO(num, vsel_m, enable_m) { \
|
||||
.name = "SAFEOUT"#num, \
|
||||
.of_match = of_match_ptr("SAFEOUT"#num), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.id = MAX77818_SAFEOUT##num, \
|
||||
.ops = &max77818_safeout_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.owner = THIS_MODULE, \
|
||||
.n_voltages = ARRAY_SIZE(max77818_safeout_volt_table), \
|
||||
.volt_table = max77818_safeout_volt_table, \
|
||||
.vsel_reg = REG_SAFEOUTCTRL, \
|
||||
.vsel_mask = (vsel_m), \
|
||||
.enable_reg = REG_SAFEOUTCTRL, \
|
||||
.enable_mask = (enable_m), \
|
||||
}
|
||||
|
||||
static const struct regulator_desc max77818_safeout_descs[] = {
|
||||
REGULATOR_DESC_SFO(1, BIT_SAFEOUT1, BIT_ENSAFEOUT1),
|
||||
REGULATOR_DESC_SFO(2, BIT_SAFEOUT2, BIT_ENSAFEOUT2),
|
||||
};
|
||||
|
||||
static int max77818_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77818_dev *max77818 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct regulator_config config = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
int i, ret;
|
||||
|
||||
config.dev = max77818->dev;
|
||||
config.driver_data = max77818;
|
||||
config.regmap = max77818->regmap_pmic;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(max77818_safeout_descs); i++) {
|
||||
struct regulator_dev *rdev;
|
||||
|
||||
rdev = devm_regulator_register(dev, &max77818_safeout_descs[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
ret = PTR_ERR(rdev);
|
||||
dev_err(dev, "regulator init failed for %s: %d\n",
|
||||
max77818_safeout_descs[i].name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver max77818_regulator_driver = {
|
||||
.driver = {
|
||||
.name = MAX77818_REGULATOR_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max77818_regulator_probe,
|
||||
};
|
||||
module_platform_driver(max77818_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("TaiEup Kim<clark.kim@maximintegrated.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MAXIM 77818 Regulator Driver");
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Functions to access SY3686A power management chip voltages
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; 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/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/mfd/sy7636a.h>
|
||||
|
||||
static int get_vcom_voltage_op(struct regulator_dev *rdev)
|
||||
{
|
||||
int ret;
|
||||
ret = get_vcom_voltage_mv(rdev->regmap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret * 1000;
|
||||
}
|
||||
|
||||
static int disable_regulator(struct regulator_dev *rdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(rdev->dev.parent);
|
||||
int ret = 0;
|
||||
mutex_lock(&sy7636a->reglock);
|
||||
ret = regulator_disable_regmap(rdev);
|
||||
usleep_range(30000, 35000);
|
||||
mutex_unlock(&sy7636a->reglock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sy7636a_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(rdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sy7636a->reglock);
|
||||
ret = regulator_is_enabled_regmap(rdev);
|
||||
mutex_unlock(&sy7636a->reglock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int enable_regulator_pgood(struct regulator_dev *rdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(rdev->dev.parent);
|
||||
int pwr_good = 0;
|
||||
int ret = 0;
|
||||
unsigned long t0, t1;
|
||||
const unsigned int wait_time = 500;
|
||||
unsigned int wait_cnt;
|
||||
|
||||
t0 = jiffies;
|
||||
|
||||
mutex_lock(&sy7636a->reglock);
|
||||
|
||||
ret = regulator_enable_regmap(rdev);
|
||||
if (ret) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
for (wait_cnt = 0; wait_cnt < wait_time; wait_cnt++) {
|
||||
pwr_good = gpiod_get_value_cansleep(sy7636a->pgood_gpio);
|
||||
if (pwr_good < 0) {
|
||||
dev_err(&rdev->dev, "Failed to read pgood gpio: %d\n", pwr_good);
|
||||
ret = pwr_good;
|
||||
goto finish;
|
||||
}
|
||||
else if (pwr_good)
|
||||
break;
|
||||
|
||||
usleep_range(1000, 1500);
|
||||
}
|
||||
|
||||
t1 = jiffies;
|
||||
|
||||
if (!pwr_good) {
|
||||
dev_err(&rdev->dev, "Power good signal timeout after %u ms\n",
|
||||
jiffies_to_msecs(t1 - t0));
|
||||
ret = -ETIME;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
dev_dbg(&rdev->dev, "Power good OK (took %u ms, %u waits)\n",
|
||||
jiffies_to_msecs(t1 - t0),
|
||||
wait_cnt);
|
||||
|
||||
finish:
|
||||
mutex_unlock(&sy7636a->reglock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct regulator_ops sy7636a_vcom_volt_ops = {
|
||||
.get_voltage = get_vcom_voltage_op,
|
||||
.enable = enable_regulator_pgood,
|
||||
.disable = disable_regulator,
|
||||
.is_enabled = sy7636a_regulator_is_enabled,
|
||||
};
|
||||
|
||||
struct regulator_desc desc = {
|
||||
.name = "vcom",
|
||||
.id = 0,
|
||||
.ops = &sy7636a_vcom_volt_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.enable_reg = SY7636A_REG_OPERATION_MODE_CRL,
|
||||
.enable_mask = SY7636A_OPERATION_MODE_CRL_ONOFF,
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.of_match = of_match_ptr("vcom"),
|
||||
};
|
||||
|
||||
static int sy7636a_regulator_init(struct sy7636a *sy7636a)
|
||||
{
|
||||
return regmap_write(sy7636a->regmap,
|
||||
SY7636A_REG_POWER_ON_DELAY_TIME,
|
||||
0x0);
|
||||
}
|
||||
|
||||
static int sy7636a_regulator_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = get_vcom_voltage_mv(sy7636a->regmap);
|
||||
|
||||
if (ret > 0)
|
||||
sy7636a->vcom = (unsigned int)ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sy7636a_regulator_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(dev->parent);
|
||||
|
||||
if (!sy7636a->vcom || sy7636a->vcom > 5000) {
|
||||
dev_warn(dev, "Vcom value invalid, and thus not restored\n");
|
||||
}
|
||||
else {
|
||||
ret = set_vcom_voltage_mv(sy7636a->regmap, sy7636a->vcom);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sy7636a_regulator_init(sy7636a);
|
||||
}
|
||||
|
||||
static int sy7636a_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(pdev->dev.parent);
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *rdev;
|
||||
struct gpio_desc *gdp;
|
||||
int ret;
|
||||
|
||||
if (!sy7636a)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
platform_set_drvdata(pdev, sy7636a);
|
||||
|
||||
mutex_init(&sy7636a->reglock);
|
||||
|
||||
gdp = devm_gpiod_get(sy7636a->dev, "epd-pwr-good", GPIOD_IN);
|
||||
if (IS_ERR(gdp)) {
|
||||
dev_err(sy7636a->dev, "Power good GPIO fault %ld\n", PTR_ERR(gdp));
|
||||
return PTR_ERR(gdp);
|
||||
}
|
||||
|
||||
sy7636a->pgood_gpio = gdp;
|
||||
dev_info(sy7636a->dev,
|
||||
"Power good GPIO registered (gpio# %d)\n",
|
||||
desc_to_gpio(sy7636a->pgood_gpio));
|
||||
|
||||
ret = sy7636a_regulator_init(sy7636a);
|
||||
if (ret) {
|
||||
dev_err(sy7636a->dev, "Failed to initialize regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.dev->of_node = sy7636a->dev->of_node;
|
||||
config.driver_data = sy7636a;
|
||||
config.regmap = sy7636a->regmap;
|
||||
|
||||
rdev = devm_regulator_register(&pdev->dev, &desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(sy7636a->dev, "Failed to register %s regulator\n",
|
||||
pdev->name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id sy7636a_regulator_id_table[] = {
|
||||
{ "sy7636a-regulator", },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sy7636a_regulator_id_table);
|
||||
|
||||
static const struct dev_pm_ops sy7636a_pm_ops = {
|
||||
.suspend = sy7636a_regulator_suspend,
|
||||
.resume = sy7636a_regulator_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver sy7636a_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "sy7636a-regulator",
|
||||
.pm = &sy7636a_pm_ops,
|
||||
},
|
||||
.probe = sy7636a_regulator_probe,
|
||||
.id_table = sy7636a_regulator_id_table,
|
||||
};
|
||||
module_platform_driver(sy7636a_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>");
|
||||
MODULE_DESCRIPTION("SY7636A voltage regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -700,6 +700,15 @@ config RTC_DRV_SD3078
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-sd3078
|
||||
|
||||
config RTC_DRV_BD7181X
|
||||
tristate "BD71815/BD71817 RTC"
|
||||
depends on RTC_CLASS && MFD_BD7181X
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
RoHM BD71815/BD71817 chips.
|
||||
|
||||
This driver can also be built as a module.
|
||||
|
||||
endif # I2C
|
||||
|
||||
comment "SPI RTC drivers"
|
||||
|
|
|
@ -39,6 +39,9 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
|
|||
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
||||
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
||||
obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.o
|
||||
obj-$(CONFIG_RTC_DRV_BD7181X) += rtc-bd7181x.o
|
||||
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
|
||||
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
||||
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* @file RoHM BD71815/BD71817 Real Time Clock interface
|
||||
*
|
||||
* Copyright (C) 2014 Embest Technology Co. Ltd. Inc.
|
||||
*
|
||||
* @author Peter Yang <yanglsh@embest-tech.com>
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
//#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/bd7181x.h>
|
||||
|
||||
/** @brief bd7181x rtc struct */
|
||||
struct bd7181x_rtc {
|
||||
struct rtc_device *rtc; /**< system rtc device */
|
||||
int irq; /**< rtc irq */
|
||||
};
|
||||
|
||||
/* @brief Total number of RTC registers needed to set time*/
|
||||
// #define NUM_TIME_REGS (BD7181X_REG_YEAR - BD7181X_REG_SEC + 1)
|
||||
|
||||
/**@brief enable or disable rtc alarm irq
|
||||
* @param dev rtc device of system
|
||||
* @param enabled enable if non-zero
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
|
||||
{
|
||||
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
|
||||
u8 val = 0;
|
||||
|
||||
if (enabled)
|
||||
val = ALM0;
|
||||
|
||||
return regmap_write(mfd->regmap, BD7181X_REG_INT_EN_12, val);
|
||||
}
|
||||
|
||||
/**@brief bd7181x rtc time convert to linux time
|
||||
* @param tm linux rtc time
|
||||
* @param hw_rtc bd7181x rtc time
|
||||
* @return argument tm
|
||||
*/
|
||||
static struct rtc_time* hw_to_rtc_time(struct rtc_time* tm, const struct bd7181x_rtc_alarm* hw_rtc) {
|
||||
u8 hour;
|
||||
|
||||
tm->tm_sec = bcd2bin(hw_rtc->sec);
|
||||
tm->tm_min = bcd2bin(hw_rtc->min);
|
||||
hour = hw_rtc->hour & ~HOUR_24HOUR;
|
||||
tm->tm_hour = bcd2bin(hour);
|
||||
tm->tm_mday = bcd2bin(hw_rtc->day);
|
||||
tm->tm_mon = bcd2bin(hw_rtc->month) - 1;
|
||||
tm->tm_year = bcd2bin(hw_rtc->year) + 100;
|
||||
return tm;
|
||||
}
|
||||
|
||||
/**@brief linux time convert bd7181x rtc time
|
||||
* @param hw_rtc bd7181x rtc time
|
||||
* @param tm linux rtc time
|
||||
* @return argument hw_rtc
|
||||
*/
|
||||
static struct bd7181x_rtc_alarm* rtc_time_to_hw(struct bd7181x_rtc_alarm* hw_rtc, const struct rtc_time* tm) {
|
||||
hw_rtc->sec = bin2bcd(tm->tm_sec);
|
||||
hw_rtc->min = bin2bcd(tm->tm_min);
|
||||
hw_rtc->hour = HOUR_24HOUR | bin2bcd(tm->tm_hour);
|
||||
hw_rtc->day = bin2bcd(tm->tm_mday);
|
||||
hw_rtc->month = bin2bcd(tm->tm_mon + 1);
|
||||
hw_rtc->year = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
return hw_rtc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets current bd7181x RTC time and date parameters.
|
||||
*
|
||||
* The RTC's time/alarm representation is not what gmtime(3) requires
|
||||
* Linux to use:
|
||||
*
|
||||
* - Months are 1..12 vs Linux 0-11
|
||||
* - Years are 0..99 vs Linux 1900..N (we assume 21st century)
|
||||
*/
|
||||
/**@brief read date/time from bd7181x rtc
|
||||
* @param dev rtc device of system
|
||||
* @param tm date/time store target
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct bd7181x_rtc_alarm rtc_data[1];
|
||||
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(mfd->regmap, BD7181X_REG_SEC, rtc_data, sizeof rtc_data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "reading from RTC failed with err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hw_to_rtc_time(tm, rtc_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**@brief write date/time to bd7181x rtc
|
||||
* @param dev rtc device of system
|
||||
* @param tm date/time source
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct bd7181x_rtc_alarm rtc_data[1];
|
||||
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
rtc_time_to_hw(rtc_data, tm);
|
||||
|
||||
/* update all the time registers in one shot */
|
||||
ret = regmap_bulk_write(mfd->regmap, BD7181X_REG_SEC, rtc_data, sizeof rtc_data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "rtc_set_time error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**@brief Gets current bd7181x RTC alarm time.
|
||||
* @param dev rtc device of system
|
||||
* @param alm alarm date/time store target
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct bd7181x_rtc_alarm rtc_data[1];
|
||||
u32 int_val;
|
||||
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(mfd->regmap, BD7181X_REG_ALM0_SEC, rtc_data, sizeof rtc_data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "rtc_read_alarm error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hw_to_rtc_time(&alm->time, rtc_data);
|
||||
|
||||
ret = regmap_read(mfd->regmap, BD7181X_REG_INT_EN_12, &int_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (int_val & ALM0)
|
||||
alm->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**@brief Set current bd7181x RTC alarm time
|
||||
* @param dev rtc device of system
|
||||
* @param alm alarm date/time to set
|
||||
* @retval 0 success
|
||||
* @retval negative error number
|
||||
*/
|
||||
static int bd7181x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct bd7181x_rtc_alarm rtc_data[1];
|
||||
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
// printk("%s() L%d\n", __func__, __LINE__);
|
||||
|
||||
ret = bd7181x_rtc_alarm_irq_enable(dev, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_time_to_hw(rtc_data, &alm->time);
|
||||
|
||||
/* update all the alarm registers in one shot */
|
||||
ret = regmap_bulk_write(mfd->regmap, BD7181X_REG_ALM0_SEC, rtc_data, sizeof rtc_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "rtc_set_alarm error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (alm->enabled)
|
||||
ret = bd7181x_rtc_alarm_irq_enable(dev, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**@brief bd7181x rtc alarm interrupt
|
||||
* @param irq system irq
|
||||
* @param rtc rtc device of system
|
||||
* @retval IRQ_HANDLED success
|
||||
* @retval IRQ_NONE error
|
||||
*/
|
||||
static irqreturn_t bd7181x_rtc_interrupt(int irq, void *rtc)
|
||||
{
|
||||
struct device *dev = rtc;
|
||||
unsigned long events = 0;
|
||||
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
|
||||
struct bd7181x_rtc *bd_rtc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
u32 rtc_reg;
|
||||
|
||||
dev_info(mfd->dev, "bd7181x_rtc_interrupt() in.\n");
|
||||
|
||||
ret = regmap_read(mfd->regmap, BD7181X_REG_INT_STAT_12, &rtc_reg);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
dev_info(mfd->dev, "BD7181X_REG_INT_STAT_12=0x%x\n", rtc_reg);
|
||||
|
||||
if (rtc_reg & ALM0)
|
||||
events = RTC_IRQF | RTC_AF;
|
||||
|
||||
ret = regmap_write(mfd->regmap, BD7181X_REG_INT_STAT_12, rtc_reg);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
dev_info(mfd->dev, "\n~~~IRQ ALARM.\n");
|
||||
|
||||
/* Notify RTC core on event */
|
||||
rtc_update_irq(bd_rtc->rtc, 1, events);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/** @brief function operations definition */
|
||||
static struct rtc_class_ops bd7181x_rtc_ops = {
|
||||
.read_time = bd7181x_rtc_read_time,
|
||||
.set_time = bd7181x_rtc_set_time,
|
||||
.read_alarm = bd7181x_rtc_read_alarm,
|
||||
.set_alarm = bd7181x_rtc_set_alarm,
|
||||
.alarm_irq_enable = bd7181x_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
/**@brief probe bd7181x rtc device
|
||||
@param pdev bd7181x rtc platform device
|
||||
@retval 0 success
|
||||
@retval negative fail
|
||||
*/
|
||||
static int bd7181x_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bd7181x *bd7181x = NULL;
|
||||
struct bd7181x_rtc *bd_rtc = NULL;
|
||||
int ret;
|
||||
int irq;
|
||||
u32 rtc_reg;
|
||||
|
||||
bd7181x = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
bd_rtc = devm_kzalloc(&pdev->dev, sizeof(struct bd7181x_rtc),
|
||||
GFP_KERNEL);
|
||||
if (!bd_rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Clear pending interrupts */
|
||||
ret = regmap_read(bd7181x->regmap, BD7181X_REG_INT_STAT_12, &rtc_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(bd7181x->regmap, BD7181X_REG_INT_STAT_12, rtc_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "Enabling rtc-bd7181x.\n");
|
||||
|
||||
#if 0
|
||||
/* Enable RTC alarm interrupt */
|
||||
ret = regmap_update_bits(bd7181x->regmap, BD7181X_REG_INT_EN_00, ALMALE, ALMALE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif
|
||||
/* Enable ALM0_EN mask */
|
||||
ret = regmap_write(bd7181x->regmap, BD7181X_REG_ALM0_MASK, ALM0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", irq);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
bd7181x_rtc_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME,
|
||||
dev_name(&pdev->dev), &pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "IRQ is not free.\n");
|
||||
return ret;
|
||||
}
|
||||
bd_rtc->irq = irq;
|
||||
device_set_wakeup_capable(&pdev->dev, 1);
|
||||
|
||||
bd_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&bd7181x_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(bd_rtc->rtc)) {
|
||||
ret = PTR_ERR(bd_rtc->rtc);
|
||||
dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, bd_rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable bd7181x RTC interrupts.
|
||||
* Sets status flag to free.
|
||||
*/
|
||||
/**@brief remove bd7181x rtc device
|
||||
@param pdev bd7181x rtc platform device
|
||||
@return 0
|
||||
*/
|
||||
static int bd7181x_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
bd7181x_rtc_alarm_irq_enable(&pdev->dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**@brief shutdown bd7181x rtc device
|
||||
@param pdev bd7181x rtc platform device
|
||||
@return void
|
||||
*/
|
||||
static void bd7181x_rtc_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
/* mask timer interrupts, but leave alarm interrupts on to enable
|
||||
power-on when alarm is triggered */
|
||||
bd7181x_rtc_alarm_irq_enable(&pdev->dev, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**@brief suspend bd7181x rtc device
|
||||
* @param dev rtc device of system
|
||||
* @retval 0
|
||||
*/
|
||||
static int bd7181x_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct bd7181x_rtc *bd_rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(bd_rtc->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**@brief resume bd7181x rtc device
|
||||
* @param dev rtc device of system
|
||||
* @retval 0
|
||||
*/
|
||||
static int bd7181x_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct bd7181x_rtc *bd_rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(bd_rtc->irq);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bd7181x_rtc_pm_ops, bd7181x_rtc_suspend, bd7181x_rtc_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bd7181x_rtc_of_match[] = {
|
||||
{.compatible = "ti,bd7181x-rtc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bd7181x_rtc_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver bd7181xrtc_driver = {
|
||||
.probe = bd7181x_rtc_probe,
|
||||
.remove = bd7181x_rtc_remove,
|
||||
.shutdown = bd7181x_rtc_shutdown,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bd7181x-rtc",
|
||||
.pm = &bd7181x_rtc_pm_ops,
|
||||
.of_match_table = of_match_ptr(bd7181x_rtc_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
/**@brief module initialize function */
|
||||
static int __init bd7181x_rtc_init(void)
|
||||
{
|
||||
return platform_driver_register(&bd7181xrtc_driver);
|
||||
}
|
||||
module_init(bd7181x_rtc_init);
|
||||
|
||||
/**@brief module deinitialize function */
|
||||
static void __exit bd7181x_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bd7181xrtc_driver);
|
||||
}
|
||||
module_exit(bd7181x_rtc_exit);
|
||||
|
||||
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:bd7181x-rtc");
|
|
@ -403,6 +403,13 @@ depends on (ARCH_STI || ARCH_STM32) && OF
|
|||
source "drivers/thermal/st/Kconfig"
|
||||
endmenu
|
||||
|
||||
config SY7636A_THERMAL
|
||||
tristate "SY7636A thermal management"
|
||||
depends on MFD_SY7636A
|
||||
help
|
||||
Enable the sy7636a thermal driver, which supports the
|
||||
temperature sensor embedded in Silabs SY7636A chip.
|
||||
|
||||
config TANGO_THERMAL
|
||||
tristate "Tango thermal management"
|
||||
depends on ARCH_TANGO || COMPILE_TEST
|
||||
|
|
|
@ -50,6 +50,7 @@ obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
|
|||
obj-y += intel/
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-y += st/
|
||||
obj-$(CONFIG_SY7636A_THERMAL) += sy7636a_thermal.o
|
||||
obj-$(CONFIG_QCOM_TSENS) += qcom/
|
||||
obj-y += tegra/
|
||||
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Functions to access SY3686A power management chip temperature
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; 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/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include <linux/mfd/sy7636a.h>
|
||||
|
||||
struct sy7636a_data {
|
||||
struct sy7636a *sy7636a;
|
||||
struct thermal_zone_device *thermal_zone_dev;
|
||||
};
|
||||
|
||||
static int sy7636a_get_temp(void *arg, int *res)
|
||||
{
|
||||
unsigned int reg_val, mode_ctr;
|
||||
int ret;
|
||||
struct sy7636a_data *data = arg;
|
||||
bool isVoltageActive;
|
||||
|
||||
mutex_lock(&data->sy7636a->reglock);
|
||||
|
||||
ret = regmap_read(data->sy7636a->regmap,
|
||||
SY7636A_REG_OPERATION_MODE_CRL, &mode_ctr);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
isVoltageActive = mode_ctr & SY7636A_OPERATION_MODE_CRL_ONOFF;
|
||||
|
||||
if (!isVoltageActive) {
|
||||
ret = regmap_write(data->sy7636a->regmap,
|
||||
SY7636A_REG_OPERATION_MODE_CRL,
|
||||
mode_ctr | SY7636A_OPERATION_MODE_CRL_ONOFF);
|
||||
if (ret)
|
||||
goto done;
|
||||
usleep_range(1000, 1500);
|
||||
}
|
||||
|
||||
ret = regmap_read(data->sy7636a->regmap,
|
||||
SY7636A_REG_TERMISTOR_READOUT, ®_val);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
if (!isVoltageActive) {
|
||||
ret = regmap_write(data->sy7636a->regmap,
|
||||
SY7636A_REG_OPERATION_MODE_CRL,
|
||||
mode_ctr);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
*res = *((signed char*)®_val);
|
||||
*res *= 1000;
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->sy7636a->reglock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops ops = {
|
||||
.get_temp = sy7636a_get_temp,
|
||||
};
|
||||
|
||||
static int sy7636a_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(pdev->dev.parent);
|
||||
struct sy7636a_data *data;
|
||||
|
||||
if (!sy7636a)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct sy7636a_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
data->sy7636a = sy7636a;
|
||||
data->thermal_zone_dev = devm_thermal_zone_of_sensor_register(
|
||||
pdev->dev.parent,
|
||||
0,
|
||||
data,
|
||||
&ops);
|
||||
|
||||
return PTR_ERR_OR_ZERO(data->thermal_zone_dev);
|
||||
}
|
||||
|
||||
static const struct platform_device_id sy7636a_thermal_id_table[] = {
|
||||
{ "sy7636a-thermal", },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sy7636a_thermal_id_table);
|
||||
|
||||
static struct platform_driver sy7636a_thermal_driver = {
|
||||
.driver = {
|
||||
.name = "sy7636a-thermal",
|
||||
},
|
||||
.probe = sy7636a_thermal_probe,
|
||||
.id_table = sy7636a_thermal_id_table,
|
||||
};
|
||||
module_platform_driver(sy7636a_thermal_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>");
|
||||
MODULE_DESCRIPTION("SY7636A thermal driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -128,6 +128,59 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
|
|||
return role;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handling vbus glitch
|
||||
* We only need to consider glitch for without usb connection,
|
||||
* With usb connection, we consider it as real disconnection.
|
||||
*
|
||||
* If the vbus can't be kept above B session valid for timeout value,
|
||||
* we think it is a vbus glitch, otherwise it's a valid vbus.
|
||||
*/
|
||||
#define CI_VBUS_CONNECT_GLITCH_TIMEOUT_MS 300
|
||||
#define CI_VBUS_CONNECT_TOTAL_TIMEOUT_MS 1000
|
||||
#define CI_VBUS_GLITCH_COUNTER_RESET_VAL (CI_VBUS_CONNECT_GLITCH_TIMEOUT_MS/20)
|
||||
static int ci_is_vbus_glitch(struct ci_hdrc *ci)
|
||||
{
|
||||
int glitch_counter = CI_VBUS_GLITCH_COUNTER_RESET_VAL;
|
||||
int total_counter = CI_VBUS_CONNECT_TOTAL_TIMEOUT_MS/20;
|
||||
|
||||
dev_dbg(ci->dev,
|
||||
"Running for max %d ms while checking for VBUS glitc\n",
|
||||
CI_VBUS_CONNECT_TOTAL_TIMEOUT_MS);
|
||||
dev_dbg(ci->dev,
|
||||
"(expecting min %d ms with stable VBUS)\n",
|
||||
CI_VBUS_CONNECT_GLITCH_TIMEOUT_MS);
|
||||
do {
|
||||
|
||||
if (hw_read_otgsc(ci, OTGSC_AVV)) {
|
||||
dev_dbg(ci->dev, "VBUS is valid, returning\n");
|
||||
return 0;
|
||||
} else if (!hw_read_otgsc(ci, OTGSC_BSV)) {
|
||||
dev_dbg(ci->dev,
|
||||
"B session glitch, resetting glitch counter\n");
|
||||
glitch_counter = CI_VBUS_GLITCH_COUNTER_RESET_VAL;
|
||||
|
||||
dev_warn(ci->dev, "there is a vbus glitch\n");
|
||||
} else {
|
||||
glitch_counter--;
|
||||
}
|
||||
msleep(20);
|
||||
total_counter--;
|
||||
} while ((total_counter > 0) && (glitch_counter > 0));
|
||||
|
||||
if (glitch_counter > 0) {
|
||||
dev_dbg(ci->dev,
|
||||
"Unable to measure stable VBUS for %d ms\n",
|
||||
CI_VBUS_CONNECT_GLITCH_TIMEOUT_MS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dev_dbg(ci->dev,
|
||||
"%d ms with stable VBUS !\n",
|
||||
CI_VBUS_CONNECT_GLITCH_TIMEOUT_MS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ci_handle_vbus_connected(struct ci_hdrc *ci)
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -1760,7 +1760,7 @@ static const struct usb_gadget_ops usb_gadget_ops = {
|
|||
.wakeup = ci_udc_wakeup,
|
||||
.set_selfpowered = ci_udc_selfpowered,
|
||||
.pullup = ci_udc_pullup,
|
||||
.vbus_draw = ci_udc_vbus_draw,
|
||||
/*.vbus_draw = ci_udc_vbus_draw, */
|
||||
.udc_start = ci_udc_start,
|
||||
.udc_stop = ci_udc_stop,
|
||||
.match_ep = ci_udc_match_ep,
|
||||
|
|
|
@ -820,6 +820,10 @@ static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
|
|||
|
||||
/* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
dev_dbg(data->dev, "Setting up USB PHY to check whether a charger is "
|
||||
"connected to the USB port\n");
|
||||
dev_dbg(data->dev, "(NOT checking whether the USB plug has been in "
|
||||
"contact with each other)\n");
|
||||
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
|
||||
val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL;
|
||||
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
|
||||
|
@ -827,7 +831,8 @@ static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
|
|||
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
dev_dbg(data->dev, "Waiting 500 ms before reading status\n");
|
||||
usleep_range(500000, 501000);
|
||||
|
||||
/* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
|
||||
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
|
||||
|
@ -835,6 +840,9 @@ static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
|
|||
dev_dbg(data->dev, "It is a standard downstream port\n");
|
||||
usb_phy->chg_type = SDP_TYPE;
|
||||
}
|
||||
else {
|
||||
dev_dbg(data->dev, "It is NOT a standard downstream port\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -134,8 +134,10 @@ USB_ETHERNET_MODULE_PARAMETERS();
|
|||
* used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
|
||||
* the non-RNDIS configuration.
|
||||
*/
|
||||
#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
|
||||
#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
|
||||
/*#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
|
||||
/*#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
|
||||
#define RNDIS_VENDOR_NUM 0x04b3 /* IBM */
|
||||
#define RNDIS_PRODUCT_NUM 0x4010 /* Ethernet/RNDIS Gadget */
|
||||
|
||||
/* For EEM gadgets */
|
||||
#define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */
|
||||
|
|
|
@ -252,6 +252,7 @@ struct mxsfb_info {
|
|||
unsigned dotclk_delay;
|
||||
const struct mxsfb_devdata *devdata;
|
||||
struct regulator *reg_lcd;
|
||||
struct regulator *reg_lcd2;
|
||||
bool wait4vsync;
|
||||
struct completion vsync_complete;
|
||||
struct completion flip_complete;
|
||||
|
@ -268,6 +269,8 @@ struct mxsfb_info {
|
|||
#ifdef CONFIG_FB_MXC_OVERLAY
|
||||
struct mxsfb_layer overlay;
|
||||
#endif
|
||||
|
||||
bool prevent_frying_pan;
|
||||
};
|
||||
|
||||
#define mxsfb_is_v3(host) (host->devdata->ipversion == 3)
|
||||
|
@ -667,7 +670,7 @@ static int mxsfb_check_var(struct fb_var_screeninfo *var,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_enable_controller(struct fb_info *fb_info)
|
||||
static int mxsfb_enable_controller(struct fb_info *fb_info)
|
||||
{
|
||||
struct mxsfb_info *host = fb_info->par;
|
||||
u32 reg;
|
||||
|
@ -683,7 +686,7 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
|
|||
if (ret < 0) {
|
||||
dev_err(&host->pdev->dev, "failed to setup"
|
||||
"dispdrv:%s\n", host->dispdrv->drv->name);
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
host->sync = fb_info->var.sync;
|
||||
}
|
||||
|
@ -693,7 +696,16 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
|
|||
if (ret) {
|
||||
dev_err(&host->pdev->dev,
|
||||
"lcd regulator enable failed: %d\n", ret);
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->reg_lcd2) {
|
||||
ret = regulator_enable(host->reg_lcd2);
|
||||
if (ret) {
|
||||
dev_err(&host->pdev->dev,
|
||||
"lcd2 regulator enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -725,7 +737,14 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
|
|||
"lcd regulator disable failed: %d\n",
|
||||
ret);
|
||||
}
|
||||
return;
|
||||
if (host->reg_lcd2) {
|
||||
ret = regulator_disable(host->reg_lcd2);
|
||||
if (ret)
|
||||
dev_err(&host->pdev->dev,
|
||||
"lcd2 regulator disable failed: %d\n",
|
||||
ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
clk_enable_pix(host);
|
||||
#ifdef CONFIG_FB_IMX64_DEBUG
|
||||
|
@ -752,6 +771,7 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
|
|||
|
||||
host->enabled = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_disable_controller(struct fb_info *fb_info)
|
||||
|
@ -795,6 +815,12 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
|
|||
dev_err(&host->pdev->dev,
|
||||
"lcd regulator disable failed: %d\n", ret);
|
||||
}
|
||||
if (host->reg_lcd2) {
|
||||
ret = regulator_disable(host->reg_lcd2);
|
||||
if (ret)
|
||||
dev_err(&host->pdev->dev,
|
||||
"lcd2 regulator disable failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -966,6 +992,9 @@ static int mxsfb_set_par(struct fb_info *fb_info)
|
|||
writel(fb_info->fix.smem_start +
|
||||
fb_info->fix.line_length * fb_info->var.yoffset,
|
||||
host->base + host->devdata->next_buf);
|
||||
writel(fb_info->fix.smem_start +
|
||||
fb_info->fix.line_length * fb_info->var.yoffset,
|
||||
host->base + host->devdata->cur_buf);
|
||||
|
||||
if (reenable)
|
||||
mxsfb_enable_controller(fb_info);
|
||||
|
@ -1066,11 +1095,14 @@ static int mxsfb_ioctl(struct fb_info *fb_info, unsigned int cmd,
|
|||
static int mxsfb_blank(int blank, struct fb_info *fb_info)
|
||||
{
|
||||
struct mxsfb_info *host = fb_info->par;
|
||||
int ret = 0;
|
||||
int prev_blank;
|
||||
|
||||
#ifdef CONFIG_FB_IMX64_DEBUG
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
prev_blank = host->cur_blank;
|
||||
host->cur_blank = blank;
|
||||
|
||||
switch (blank) {
|
||||
|
@ -1101,11 +1133,13 @@ static int mxsfb_blank(int blank, struct fb_info *fb_info)
|
|||
|
||||
writel(0, host->base + LCDC_CTRL);
|
||||
mxsfb_set_par(host->fb_info);
|
||||
mxsfb_enable_controller(fb_info);
|
||||
ret = mxsfb_enable_controller(fb_info);
|
||||
if (ret)
|
||||
host->cur_blank = prev_blank;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxsfb_pan_display(struct fb_var_screeninfo *var,
|
||||
|
@ -1143,13 +1177,21 @@ static int mxsfb_pan_display(struct fb_var_screeninfo *var,
|
|||
host->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
ret = wait_for_completion_timeout(&host->flip_complete, HZ / 2);
|
||||
|
||||
if (!ret) {
|
||||
dev_err(fb_info->device,
|
||||
"mxs wait for pan flip timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (host->prevent_frying_pan) {
|
||||
/* Next frame will be the zero frame (last frame in buffer) by default. */
|
||||
offset = fb_info->fix.line_length * (var->yres_virtual - var->yres);
|
||||
writel(fb_info->fix.smem_start + offset,
|
||||
host->base + host->devdata->next_buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxsfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
|
@ -2235,6 +2277,34 @@ static int mxsfb_probe(struct platform_device *pdev)
|
|||
host->fb_info = fb_info;
|
||||
fb_info->par = host;
|
||||
|
||||
host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");
|
||||
if (IS_ERR(host->reg_lcd)) {
|
||||
ret = PTR_ERR(host->reg_lcd);
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
dev_warn(&pdev->dev, "lcd-supply not ready, deferring\n");
|
||||
goto fb_release;
|
||||
} else {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get lcd-supply (errno %d), ignoring\n",
|
||||
ret);
|
||||
host->reg_lcd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
host->reg_lcd2 = devm_regulator_get(&pdev->dev, "lcd2");
|
||||
if (IS_ERR(host->reg_lcd2)) {
|
||||
ret = PTR_ERR(host->reg_lcd2);
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
dev_warn(&pdev->dev, "lcd2-supply not ready, deferring\n");
|
||||
goto fb_release;
|
||||
} else {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get lcd2-supply (errno %d), ignoring\n",
|
||||
ret);
|
||||
host->reg_lcd2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,
|
||||
dev_name(&pdev->dev), host);
|
||||
if (ret) {
|
||||
|
@ -2279,10 +2349,6 @@ static int mxsfb_probe(struct platform_device *pdev)
|
|||
goto fb_release;
|
||||
}
|
||||
|
||||
host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");
|
||||
if (IS_ERR(host->reg_lcd))
|
||||
host->reg_lcd = NULL;
|
||||
|
||||
fb_info->pseudo_palette = devm_kcalloc(&pdev->dev, 16, sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
if (!fb_info->pseudo_palette) {
|
||||
|
@ -2332,6 +2398,10 @@ static int mxsfb_probe(struct platform_device *pdev)
|
|||
|
||||
mxsfb_overlay_init(host);
|
||||
|
||||
host->prevent_frying_pan = of_property_read_bool(pdev->dev.of_node, "prevent-frying-pan");
|
||||
if (host->prevent_frying_pan)
|
||||
dev_info(&pdev->dev, "Driver set to prevent frying pan mode\n");
|
||||
|
||||
#ifndef CONFIG_FB_IMX64_DEBUG
|
||||
console_lock();
|
||||
ret = fb_blank(fb_info, FB_BLANK_UNBLANK);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
|
@ -378,6 +379,8 @@ static int imx2_wdt_suspend(struct device *dev)
|
|||
|
||||
clk_disable_unprepare(wdev->clk);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -388,6 +391,8 @@ static int imx2_wdt_resume(struct device *dev)
|
|||
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = clk_prepare_enable(wdev->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* cyttsp5_platform.h
|
||||
* Parade TrueTouch(TM) Standard Product V5 Platform Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2013-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_CYTTSP5_PLATFORM_H
|
||||
#define _LINUX_CYTTSP5_PLATFORM_H
|
||||
|
||||
#include <linux/platform_data/cyttsp5.h>
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) \
|
||||
|| defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MODULE)
|
||||
extern struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data;
|
||||
|
||||
int cyttsp5_xres(struct cyttsp5_core_platform_data *pdata, struct device *dev);
|
||||
int cyttsp5_init(struct cyttsp5_core_platform_data *pdata, int on,
|
||||
struct device *dev);
|
||||
int cyttsp5_power(struct cyttsp5_core_platform_data *pdata, int on,
|
||||
struct device *dev, atomic_t *ignore_irq);
|
||||
#ifdef CYTTSP5_DETECT_HW
|
||||
int cyttsp5_detect(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev, cyttsp5_platform_read read);
|
||||
#else
|
||||
#define cyttsp5_detect NULL
|
||||
#endif
|
||||
int cyttsp5_irq_stat(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev);
|
||||
#else /* !CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 */
|
||||
static struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data;
|
||||
#define cyttsp5_xres NULL
|
||||
#define cyttsp5_init NULL
|
||||
#define cyttsp5_power NULL
|
||||
#define cyttsp5_irq_stat NULL
|
||||
#define cyttsp5_detect NULL
|
||||
#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 */
|
||||
|
||||
#endif /* _LINUX_CYTTSP5_PLATFORM_H */
|
|
@ -0,0 +1,601 @@
|
|||
/**
|
||||
* @file bd7181x.h ROHM BD71815GW/BD71817GW header file
|
||||
*
|
||||
* Copyright 2014 Embest Technology Co. Ltd. Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author yanglsh@embest-tech.com
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_BD7181X_H
|
||||
#define __LINUX_MFD_BD7181X_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
// LDO5VSEL_EQ_H
|
||||
// define to 1 when LDO5VSEL connect to High
|
||||
// define to 0 when LDO5VSEL connect to Low
|
||||
#define LDO5VSEL_EQ_H 1
|
||||
|
||||
#ifndef LDO5VSEL_EQ_H
|
||||
#error define LDO5VSEL_EQ_H to 1 when connect to High, to 0 when connect to Low
|
||||
#else
|
||||
#if LDO5VSEL_EQ_H == 1
|
||||
#define BD7181X_REG_LDO5_VOLT BD7181X_REG_LDO5_VOLT_H
|
||||
#elif LDO5VSEL_EQ_H == 0
|
||||
#define BD7181X_REG_LDO5_VOLT BD7181X_REG_LDO5_VOLT_L
|
||||
#else
|
||||
#error Define LDO5VSEL_EQ_H only to 0 or 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BD7181X_BUCK1 = 0,
|
||||
BD7181X_BUCK2,
|
||||
BD7181X_BUCK3,
|
||||
BD7181X_BUCK4,
|
||||
BD7181X_BUCK5,
|
||||
// General Purpose
|
||||
BD7181X_LDO1,
|
||||
BD7181X_LDO2,
|
||||
BD7181X_LDO3,
|
||||
// LDOs for SD Card and SD Card Interface
|
||||
BD7181X_LDO4,
|
||||
BD7181X_LDO5,
|
||||
// LDO for DDR Reference Voltage
|
||||
BD7181X_LDODVREF,
|
||||
// LDO for Secure Non-Volatile Storage
|
||||
// BD7181X_LDOSNVS,
|
||||
// LDO for Low-Power State Retention
|
||||
BD7181X_LDOLPSR,
|
||||
BD7181X_WLED,
|
||||
BD7181X_REGULATOR_CNT,
|
||||
};
|
||||
|
||||
#define BD7181X_SUPPLY_STATE_ENABLED 0x1
|
||||
|
||||
enum {
|
||||
BD7181X_REG_DEVICE = 0,
|
||||
BD7181X_REG_PWRCTRL,
|
||||
BD7181X_REG_BUCK1_MODE,
|
||||
BD7181X_REG_BUCK2_MODE,
|
||||
BD7181X_REG_BUCK3_MODE,
|
||||
BD7181X_REG_BUCK4_MODE,
|
||||
BD7181X_REG_BUCK5_MODE,
|
||||
BD7181X_REG_BUCK1_VOLT_H,
|
||||
// 0x08
|
||||
BD7181X_REG_BUCK1_VOLT_L,
|
||||
BD7181X_REG_BUCK2_VOLT_H,
|
||||
BD7181X_REG_BUCK2_VOLT_L,
|
||||
BD7181X_REG_BUCK3_VOLT,
|
||||
BD7181X_REG_BUCK4_VOLT,
|
||||
BD7181X_REG_BUCK5_VOLT,
|
||||
BD7181X_REG_LED_CTRL,
|
||||
BD7181X_REG_LED_DIMM,
|
||||
// 0x10
|
||||
BD7181X_REG_LDO_MODE1,
|
||||
BD7181X_REG_LDO_MODE2,
|
||||
BD7181X_REG_LDO_MODE3,
|
||||
BD7181X_REG_LDO_MODE4,
|
||||
BD7181X_REG_LDO1_VOLT,
|
||||
BD7181X_REG_LDO2_VOLT,
|
||||
BD7181X_REG_LDO3_VOLT,
|
||||
BD7181X_REG_LDO4_VOLT,
|
||||
// 0x18
|
||||
BD7181X_REG_LDO5_VOLT_H,
|
||||
BD7181X_REG_LDO5_VOLT_L,
|
||||
BD7181X_REG_BUCK_PD_DIS,
|
||||
BD7181X_REG_LDO_PD_DIS,
|
||||
BD7181X_REG_GPO,
|
||||
BD7181X_REG_OUT32K,
|
||||
BD7181X_REG_SEC,
|
||||
BD7181X_REG_MIN,
|
||||
// 0x20
|
||||
BD7181X_REG_HOUR,
|
||||
BD7181X_REG_WEEK,
|
||||
BD7181X_REG_DAY,
|
||||
BD7181X_REG_MONTH,
|
||||
BD7181X_REG_YEAR,
|
||||
BD7181X_REG_ALM0_SEC,
|
||||
|
||||
// 0x2C
|
||||
BD7181X_REG_ALM1_SEC = 0x2C,
|
||||
|
||||
// 0x33
|
||||
BD7181X_REG_ALM0_MASK = 0x33,
|
||||
BD7181X_REG_ALM1_MASK,
|
||||
BD7181X_REG_ALM2,
|
||||
BD7181X_REG_TRIM,
|
||||
BD7181X_REG_CONF,
|
||||
// 0x38
|
||||
BD7181X_REG_SYS_INIT,
|
||||
BD7181X_REG_CHG_STATE,
|
||||
BD7181X_REG_CHG_LAST_STATE,
|
||||
BD7181X_REG_BAT_STAT,
|
||||
BD7181X_REG_DCIN_STAT,
|
||||
BD7181X_REG_VSYS_STAT,
|
||||
BD7181X_REG_CHG_STAT,
|
||||
BD7181X_REG_CHG_WDT_STAT,
|
||||
// 0x40
|
||||
BD7181X_REG_BAT_TEMP,
|
||||
BD7181X_REG_IGNORE_0,
|
||||
BD7181X_REG_INHIBIT_0,
|
||||
BD7181X_REG_DCIN_CLPS,
|
||||
BD7181X_REG_VSYS_REG,
|
||||
BD7181X_REG_VSYS_MAX,
|
||||
BD7181X_REG_VSYS_MIN,
|
||||
BD7181X_REG_CHG_SET1,
|
||||
// 0x48
|
||||
BD7181X_REG_CHG_SET2,
|
||||
BD7181X_REG_CHG_WDT_PRE,
|
||||
BD7181X_REG_CHG_WDT_FST,
|
||||
BD7181X_REG_CHG_IPRE,
|
||||
BD7181X_REG_CHG_IFST,
|
||||
BD7181X_REG_CHG_IFST_TERM,
|
||||
BD7181X_REG_CHG_VPRE,
|
||||
BD7181X_REG_CHG_VBAT_1,
|
||||
// 0x50
|
||||
BD7181X_REG_CHG_VBAT_2,
|
||||
BD7181X_REG_CHG_VBAT_3,
|
||||
BD7181X_REG_CHG_LED_1,
|
||||
BD7181X_REG_VF_TH,
|
||||
BD7181X_REG_BAT_SET_1,
|
||||
BD7181X_REG_BAT_SET_2,
|
||||
BD7181X_REG_BAT_SET_3,
|
||||
BD7181X_REG_ALM_VBAT_TH_U,
|
||||
// 0x58
|
||||
BD7181X_REG_ALM_VBAT_TH_L,
|
||||
BD7181X_REG_ALM_DCIN_TH,
|
||||
BD7181X_REG_ALM_VSYS_TH,
|
||||
BD7181X_REG_VM_IBAT_U,
|
||||
BD7181X_REG_VM_IBAT_L,
|
||||
BD7181X_REG_VM_VBAT_U,
|
||||
BD7181X_REG_VM_VBAT_L,
|
||||
BD7181X_REG_VM_BTMP,
|
||||
// 0x60
|
||||
BD7181X_REG_VM_VTH,
|
||||
BD7181X_REG_VM_DCIN_U,
|
||||
BD7181X_REG_VM_DCIN_L,
|
||||
BD7181X_REG_VM_VSYS,
|
||||
BD7181X_REG_VM_VF,
|
||||
BD7181X_REG_VM_OCI_PRE_U,
|
||||
BD7181X_REG_VM_OCI_PRE_L,
|
||||
BD7181X_REG_VM_OCV_PRE_U,
|
||||
// 0x68
|
||||
BD7181X_REG_VM_OCV_PRE_L,
|
||||
BD7181X_REG_VM_OCI_PST_U,
|
||||
BD7181X_REG_VM_OCI_PST_L,
|
||||
BD7181X_REG_VM_OCV_PST_U,
|
||||
BD7181X_REG_VM_OCV_PST_L,
|
||||
BD7181X_REG_VM_SA_VBAT_U,
|
||||
BD7181X_REG_VM_SA_VBAT_L,
|
||||
BD7181X_REG_VM_SA_IBAT_U,
|
||||
// 0x70
|
||||
BD7181X_REG_VM_SA_IBAT_L,
|
||||
BD7181X_REG_CC_CTRL,
|
||||
BD7181X_REG_CC_BATCAP1_TH_U,
|
||||
BD7181X_REG_CC_BATCAP1_TH_L,
|
||||
BD7181X_REG_CC_BATCAP2_TH_U,
|
||||
BD7181X_REG_CC_BATCAP2_TH_L,
|
||||
BD7181X_REG_CC_BATCAP3_TH_U,
|
||||
BD7181X_REG_CC_BATCAP3_TH_L,
|
||||
// 0x78
|
||||
BD7181X_REG_CC_STAT,
|
||||
BD7181X_REG_CC_CCNTD_3,
|
||||
BD7181X_REG_CC_CCNTD_2,
|
||||
BD7181X_REG_CC_CCNTD_1,
|
||||
BD7181X_REG_CC_CCNTD_0,
|
||||
BD7181X_REG_CC_CURCD_U,
|
||||
BD7181X_REG_CC_CURCD_L,
|
||||
BD7181X_REG_VM_OCUR_THR_1,
|
||||
// 0x80
|
||||
BD7181X_REG_VM_OCUR_DUR_1,
|
||||
BD7181X_REG_VM_OCUR_THR_2,
|
||||
BD7181X_REG_VM_OCUR_DUR_2,
|
||||
BD7181X_REG_VM_OCUR_THR_3,
|
||||
BD7181X_REG_VM_OCUR_DUR_3,
|
||||
BD7181X_REG_VM_OCUR_MON,
|
||||
BD7181X_REG_VM_BTMP_OV_THR,
|
||||
BD7181X_REG_VM_BTMP_OV_DUR,
|
||||
// 0x88
|
||||
BD7181X_REG_VM_BTMP_LO_THR,
|
||||
BD7181X_REG_VM_BTMP_LO_DUR,
|
||||
BD7181X_REG_VM_BTMP_MON,
|
||||
BD7181X_REG_INT_EN_01,
|
||||
// 0x95
|
||||
BD7181X_REG_INT_EN_11 = 0x95,
|
||||
// 0x96
|
||||
BD7181X_REG_INT_EN_12 = 0x96,
|
||||
BD7181X_REG_INT_STAT,
|
||||
|
||||
// 0x98
|
||||
BD7181X_REG_INT_STAT_01,
|
||||
BD7181X_REG_INT_STAT_02,
|
||||
BD7181X_REG_INT_STAT_03,
|
||||
BD7181X_REG_INT_STAT_04,
|
||||
BD7181X_REG_INT_STAT_05,
|
||||
BD7181X_REG_INT_STAT_06,
|
||||
BD7181X_REG_INT_STAT_07,
|
||||
BD7181X_REG_INT_STAT_08,
|
||||
|
||||
// 0xA0
|
||||
BD7181X_REG_INT_STAT_09,
|
||||
BD7181X_REG_INT_STAT_10,
|
||||
BD7181X_REG_INT_STAT_11,
|
||||
BD7181X_REG_INT_STAT_12,
|
||||
BD7181X_REG_INT_UPDATE,
|
||||
|
||||
// 0xC0
|
||||
BD7181X_REG_VM_VSYS_U = 0xC0,
|
||||
BD7181X_REG_VM_VSYS_L,
|
||||
BD7181X_REG_VM_SA_VSYS_U,
|
||||
BD7181X_REG_VM_SA_VSYS_L,
|
||||
|
||||
// 0xD0
|
||||
BD7181X_REG_VM_SA_IBAT_MIN_U = 0xD0,
|
||||
BD7181X_REG_VM_SA_IBAT_MIN_L,
|
||||
BD7181X_REG_VM_SA_IBAT_MAX_U,
|
||||
BD7181X_REG_VM_SA_IBAT_MAX_L,
|
||||
BD7181X_REG_VM_SA_VBAT_MIN_U,
|
||||
BD7181X_REG_VM_SA_VBAT_MIN_L,
|
||||
BD7181X_REG_VM_SA_VBAT_MAX_U,
|
||||
BD7181X_REG_VM_SA_VBAT_MAX_L,
|
||||
BD7181X_REG_VM_SA_VSYS_MIN_U,
|
||||
BD7181X_REG_VM_SA_VSYS_MIN_L,
|
||||
BD7181X_REG_VM_SA_VSYS_MAX_U,
|
||||
BD7181X_REG_VM_SA_VSYS_MAX_L,
|
||||
BD7181X_REG_VM_SA_MINMAX_CLR,
|
||||
|
||||
// 0xE0
|
||||
BD7181X_REG_REX_CCNTD_3 = 0xE0,
|
||||
BD7181X_REG_REX_CCNTD_2,
|
||||
BD7181X_REG_REX_CCNTD_1,
|
||||
BD7181X_REG_REX_CCNTD_0,
|
||||
BD7181X_REG_REX_SA_VBAT_U,
|
||||
BD7181X_REG_REX_SA_VBAT_L,
|
||||
BD7181X_REG_REX_CTRL_1,
|
||||
BD7181X_REG_REX_CTRL_2,
|
||||
BD7181X_REG_FULL_CCNTD_3,
|
||||
BD7181X_REG_FULL_CCNTD_2,
|
||||
BD7181X_REG_FULL_CCNTD_1,
|
||||
BD7181X_REG_FULL_CCNTD_0,
|
||||
BD7181X_REG_FULL_CTRL,
|
||||
|
||||
// 0xF0
|
||||
BD7181X_REG_CCNTD_CHG_3 = 0xF0,
|
||||
BD7181X_REG_CCNTD_CHG_2,
|
||||
|
||||
// 0xFE
|
||||
BD7181X_REG_TEST_MODE = 0xFE,
|
||||
BD7181X_MAX_REGISTER,
|
||||
};
|
||||
|
||||
/* BD7181X_REG_BUCK1_MODE bits */
|
||||
#define BUCK1_RAMPRATE_MASK 0xC0
|
||||
#define BUCK1_RAMPRATE_10P00MV 0x0
|
||||
#define BUCK1_RAMPRATE_5P00MV 0x1
|
||||
#define BUCK1_RAMPRATE_2P50MV 0x2
|
||||
#define BUCK1_RAMPRATE_1P25MV 0x3
|
||||
|
||||
/* BD7181X_REG_BUCK1_VOLT_H bits */
|
||||
#define BUCK1_DVSSEL 0x80
|
||||
#define BUCK1_STBY_DVS 0x40
|
||||
#define BUCK1_H_MASK 0x3F
|
||||
#define BUCK1_H_DEFAULT 0x14
|
||||
|
||||
/* BD7181X_REG_BUCK1_VOLT_L bits */
|
||||
#define BUCK1_L_MASK 0x3F
|
||||
#define BUCK1_L_DEFAULT 0x14
|
||||
|
||||
/* BD7181X_REG_BUCK2_VOLT_H bits */
|
||||
#define BUCK2_DVSSEL 0x80
|
||||
#define BUCK2_STBY_DVS 0x40
|
||||
#define BUCK2_H_MASK 0x3F
|
||||
#define BUCK2_H_DEFAULT 0x14
|
||||
|
||||
/* BD7181X_REG_BUCK2_VOLT_L bits */
|
||||
#define BUCK2_L_MASK 0x3F
|
||||
#define BUCK2_L_DEFAULT 0x14
|
||||
|
||||
/* BD7181X_REG_LDO1_CTRL bits */
|
||||
#define LDO1_EN 0x01
|
||||
#define LDO2_EN 0x02
|
||||
#define LDO3_EN 0x04
|
||||
#define DVREF_EN 0x08
|
||||
#define VOSNVS_SW_EN 0x10
|
||||
#define VOLT_MASK 0x3F
|
||||
|
||||
/* BD7181X_REG_OUT32K bits */
|
||||
#define OUT32K_EN 0x01
|
||||
#define OUT32K_MODE 0x02
|
||||
|
||||
/* BD7181X_REG_BAT_STAT bits */
|
||||
#define BAT_DET 0x20
|
||||
#define BAT_DET_OFFSET 5
|
||||
#define BAT_DET_DONE 0x10
|
||||
#define VBAT_OV 0x08
|
||||
#define DBAT_DET 0x01
|
||||
|
||||
/* BD7181X_REG_VBUS_STAT bits */
|
||||
#define VBUS_DET 0x01
|
||||
|
||||
#define BUCK1_RAMPRATE_10MV_US 0x0
|
||||
#define BUCK1_RAMPRATE_5MV_US 0x1
|
||||
#define BUCK1_RAMPRATE_2P5MV_US 0x2
|
||||
#define BUCK1_RAMPRATE_1P25MV_US 0x3a
|
||||
|
||||
/* BD7181X_REG_ALM0_MASK bits */
|
||||
#define A0_ONESEC 0x80
|
||||
|
||||
/* BD7181X_REG_INT_EN_00 bits */
|
||||
#define ALMALE 0x1
|
||||
|
||||
/* BD7181X_REG_INT_STAT_03 bits */
|
||||
#define DCIN_MON_DET 0x02
|
||||
#define DCIN_MON_RES 0x01
|
||||
#define POWERON_LONG 0x04
|
||||
#define POWERON_MID 0x08
|
||||
#define POWERON_SHORT 0x10
|
||||
#define POWERON_PRESS 0x20
|
||||
|
||||
|
||||
/* BD71805_REG_INT_STAT_08 bits */
|
||||
#define VBAT_MON_DET 0x02
|
||||
#define VBAT_MON_RES 0x01
|
||||
|
||||
/* BD71805_REG_INT_STAT_11 bits */
|
||||
#define INT_STAT_11_VF_DET 0x80
|
||||
#define INT_STAT_11_VF_RES 0x40
|
||||
#define INT_STAT_11_VF125_DET 0x20
|
||||
#define INT_STAT_11_VF125_RES 0x10
|
||||
#define INT_STAT_11_OVTMP_DET 0x08
|
||||
#define INT_STAT_11_OVTMP_RES 0x04
|
||||
#define INT_STAT_11_LOTMP_DET 0x02
|
||||
#define INT_STAT_11_LOTMP_RES 0x01
|
||||
|
||||
#define VBAT_MON_DET 0x02
|
||||
#define VBAT_MON_RES 0x01
|
||||
|
||||
/* BD7181X_REG_PWRCTRL bits */
|
||||
#define RESTARTEN 0x01
|
||||
|
||||
/* BD7181X_REG_GPO bits */
|
||||
#define READY_FORCE_LOW 0x04
|
||||
|
||||
/* BD7181X_REG_CHG_SET1 bits */
|
||||
#define CHG_EN 0x01
|
||||
|
||||
/* BD7181X interrupt masks */
|
||||
enum {
|
||||
BD7181X_INT_EN_01_BUCKAST_MASK = 0x0F,
|
||||
BD7181X_INT_EN_02_DCINAST_MASK = 0x3E,
|
||||
BD7181X_INT_EN_03_DCINAST_MASK = 0x3F,
|
||||
BD7181X_INT_EN_04_VSYSAST_MASK = 0xCF,
|
||||
BD7181X_INT_EN_05_CHGAST_MASK = 0xFC,
|
||||
BD7181X_INT_EN_06_BATAST_MASK = 0xF3,
|
||||
BD7181X_INT_EN_07_BMONAST_MASK = 0xFE,
|
||||
BD7181X_INT_EN_08_BMONAST_MASK = 0x03,
|
||||
BD7181X_INT_EN_09_BMONAST_MASK = 0x07,
|
||||
BD7181X_INT_EN_10_BMONAST_MASK = 0x3F,
|
||||
BD7181X_INT_EN_11_TMPAST_MASK = 0xFF,
|
||||
BD7181X_INT_EN_12_ALMAST_MASK = 0x07,
|
||||
};
|
||||
/* BD7181X interrupt irqs */
|
||||
enum {
|
||||
BD7181X_IRQ_BUCK_01 = 0x0,
|
||||
BD7181X_IRQ_DCIN_02,
|
||||
BD7181X_IRQ_DCIN_03,
|
||||
BD7181X_IRQ_VSYS_04,
|
||||
BD7181X_IRQ_CHARGE_05,
|
||||
BD7181X_IRQ_BAT_06,
|
||||
BD7181X_IRQ_BAT_MON_07,
|
||||
BD7181X_IRQ_BAT_MON_08,
|
||||
BD7181X_IRQ_BAT_MON_09,
|
||||
BD7181X_IRQ_BAT_MON_10,
|
||||
BD7181X_IRQ_TEMPERATURE_11,
|
||||
BD7181X_IRQ_ALARM_12,
|
||||
};
|
||||
|
||||
/* BD7181X_REG_INT_EN_12 bits */
|
||||
#define ALM0 0x1
|
||||
|
||||
/* BD7181X_REG_HOUR bits */
|
||||
#define HOUR_24HOUR 0x80
|
||||
|
||||
/* BD7181X_REG_CC_CTRL bits */
|
||||
#define CCNTRST 0x80
|
||||
#define CCNTENB 0x40
|
||||
#define CCCALIB 0x20
|
||||
|
||||
/* BD7181X_REG_CHG_SET1 bits */
|
||||
#define WDT_AUTO 0x40
|
||||
|
||||
/* BD7181X_REG_CC_CURCD */
|
||||
#define CURDIR_Discharging 0x8000
|
||||
|
||||
/* BD7181X_REG_VM_SA_IBAT */
|
||||
#define IBAT_SA_DIR_Discharging 0x8000
|
||||
|
||||
/* BD7181X_REG_VM_SA_MINMAX_CLR bits */
|
||||
#define VSYS_SA_MIN_CLR 0x10
|
||||
#define VBAT_SA_MIN_CLR 0x01
|
||||
|
||||
/* BD7181X_REG_REX_CTRL_1 bits */
|
||||
#define REX_CLR 0x10
|
||||
|
||||
/* BD7181X_REG_REX_CTRL_1 bits */
|
||||
#define REX_PMU_STATE_MASK 0x04
|
||||
|
||||
/* BD7181X_REG_FULL_CTRL bits */
|
||||
#define FULL_CLR 0x10
|
||||
|
||||
/* BD7181X_REG_LED_CTRL bits */
|
||||
#define CHGDONE_LED_EN 0x10
|
||||
|
||||
/** @brief charge state enumuration */
|
||||
enum CHG_STATE {
|
||||
CHG_STATE_SUSPEND = 0x0, /**< suspend state */
|
||||
CHG_STATE_TRICKLE_CHARGE, /**< trickle charge state */
|
||||
CHG_STATE_PRE_CHARGE, /**< precharge state */
|
||||
CHG_STATE_FAST_CHARGE, /**< fast charge state */
|
||||
CHG_STATE_TOP_OFF, /**< top off state */
|
||||
CHG_STATE_DONE, /**< charge complete */
|
||||
};
|
||||
|
||||
/** @brief rtc or alarm registers structure */
|
||||
struct bd7181x_rtc_alarm {
|
||||
u8 sec;
|
||||
u8 min;
|
||||
u8 hour;
|
||||
u8 week;
|
||||
u8 day;
|
||||
u8 month;
|
||||
u8 year;
|
||||
};
|
||||
|
||||
struct bd7181x;
|
||||
|
||||
/**
|
||||
* @brief Board platform data may be used to initialize regulators.
|
||||
*/
|
||||
|
||||
struct bd7181x_board {
|
||||
struct regulator_init_data *init_data[BD7181X_REGULATOR_CNT];
|
||||
/**< regulator initialize data */
|
||||
int gpio_intr; /**< gpio connected to bd7181x INTB */
|
||||
int irq_base; /**< bd7181x sub irqs base # */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief bd7181x sub-driver chip access routines
|
||||
*/
|
||||
|
||||
struct bd7181x {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c_client;
|
||||
struct regmap *regmap;
|
||||
struct mutex io_mutex;
|
||||
unsigned int id;
|
||||
|
||||
/* IRQ Handling */
|
||||
int chip_irq; /**< bd7181x irq to host cpu */
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
|
||||
/* Client devices */
|
||||
struct bd7181x_pmic *pmic; /**< client device regulator */
|
||||
struct bd7181x_power *power; /**< client device battery */
|
||||
|
||||
struct bd7181x_board *of_plat_data;
|
||||
/**< Device node parsed board data */
|
||||
};
|
||||
|
||||
static inline int bd7181x_chip_id(struct bd7181x *bd7181x)
|
||||
{
|
||||
return bd7181x->id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief bd7181x_reg_read
|
||||
* read single register's value of bd7181x
|
||||
* @param bd7181x device to read
|
||||
* @param reg register address
|
||||
* @return register value if success
|
||||
* error number if fail
|
||||
*/
|
||||
static inline int bd7181x_reg_read(struct bd7181x *bd7181x, u8 reg)
|
||||
{
|
||||
int r, val;
|
||||
|
||||
r = regmap_read(bd7181x->regmap, reg, &val);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bd7181x_reg_write
|
||||
* write single register of bd7181x
|
||||
* @param bd7181x device to write
|
||||
* @param reg register address
|
||||
* @param val value to write
|
||||
* @retval 0 if success
|
||||
* @retval negative error number if fail
|
||||
*/
|
||||
|
||||
static inline int bd7181x_reg_write(struct bd7181x *bd7181x, u8 reg,
|
||||
unsigned int val)
|
||||
{
|
||||
return regmap_write(bd7181x->regmap, reg, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bd7181x_set_bits
|
||||
* set bits in one register of bd7181x
|
||||
* @param bd7181x device to read
|
||||
* @param reg register address
|
||||
* @param mask mask bits
|
||||
* @retval 0 if success
|
||||
* @retval negative error number if fail
|
||||
*/
|
||||
static inline int bd7181x_set_bits(struct bd7181x *bd7181x, u8 reg,
|
||||
u8 mask)
|
||||
{
|
||||
return regmap_update_bits(bd7181x->regmap, reg, mask, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bd7181x_clear_bits
|
||||
* clear bits in one register of bd7181x
|
||||
* @param bd7181x device to read
|
||||
* @param reg register address
|
||||
* @param mask mask bits
|
||||
* @retval 0 if success
|
||||
* @retval negative error number if fail
|
||||
*/
|
||||
|
||||
static inline int bd7181x_clear_bits(struct bd7181x *bd7181x, u8 reg,
|
||||
u8 mask)
|
||||
{
|
||||
return regmap_update_bits(bd7181x->regmap, reg, mask, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bd7181x_update_bits
|
||||
* update bits in one register of bd7181x
|
||||
* @param bd7181x device to read
|
||||
* @param reg register address
|
||||
* @param mask mask bits
|
||||
* @param val value to update
|
||||
* @retval 0 if success
|
||||
* @retval negative error number if fail
|
||||
*/
|
||||
|
||||
static inline int bd7181x_update_bits(struct bd7181x *bd7181x, u8 reg,
|
||||
u8 mask, u8 val)
|
||||
{
|
||||
return regmap_update_bits(bd7181x->regmap, reg, mask, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bd7181x platform data type
|
||||
*/
|
||||
struct bd7181x_gpo_plat_data {
|
||||
u32 mode; ///< gpo output mode
|
||||
int gpio_base; ///< base gpio number in system
|
||||
};
|
||||
|
||||
#define BD7181X_DBG0 0x0001
|
||||
#define BD7181X_DBG1 0x0002
|
||||
#define BD7181X_DBG2 0x0004
|
||||
#define BD7181X_DBG3 0x0008
|
||||
|
||||
extern unsigned int bd7181x_debug_mask;
|
||||
#define bd7181x_debug(debug, fmt, arg...) do { if(debug & bd7181x_debug_mask) printk("BD7181x:" fmt, ##arg);} while(0)
|
||||
|
||||
#endif /* __LINUX_MFD_BD7181X_H */
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* MAX77818 Driver Core
|
||||
*
|
||||
* Copyright (C) 2014 Maxim Integrated
|
||||
* TaiEup Kim <clark.kim@maximintegrated.com>
|
||||
*
|
||||
* Copyright and License statement to be determined with Customer.
|
||||
* GNU Public License version 2 requires software code to be
|
||||
* publically open source if the code is to be statically linked with
|
||||
* the Linux kernel binary object.
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#ifndef __MAX77818_MFD_H__
|
||||
#define __MAX77818_MFD_H__
|
||||
|
||||
#define MAX77818_REGULATOR_NAME "max77818-regulator"
|
||||
#define MAX77818_CHARGER_NAME "max77818-charger"
|
||||
#define MAX77818_FUELGAUGE_NAME "max77818-fuelgauge"
|
||||
|
||||
#define REG_INTSRC 0x22
|
||||
#define REG_INTSRCMASK 0x23
|
||||
#define BIT_CHGR_INT BIT (0)
|
||||
#define BIT_FG_INT BIT (1)
|
||||
#define BIT_SYS_INT BIT (2)
|
||||
|
||||
#define REG_SYSINTSRC 0x24
|
||||
#define REG_SYSINTMASK 0x26
|
||||
#define BIT_SYSUVLO_INT BIT (0)
|
||||
#define BIT_SYSOVLO_INT BIT (1)
|
||||
#define BIT_TSHDN_INT BIT (2)
|
||||
#define BIT_TM_INT BIT (7)
|
||||
|
||||
#define REG_CHARGER_INT 0xB0
|
||||
#define REG_CHARGER_INT_MASK 0xB1
|
||||
|
||||
#define BIT_CHG_BYP_I BIT (0)
|
||||
#define BIT_CHG_BATP_I BIT (2)
|
||||
#define BIT_CHG_BAT_I BIT (3)
|
||||
#define BIT_CHG_CHG_I BIT (4)
|
||||
#define BIT_CHG_WCIN_I BIT (5)
|
||||
#define BIT_CHG_CHGIN_I BIT (6)
|
||||
#define BIT_CHG_AICL_I BIT (7)
|
||||
|
||||
/* Chip Interrupts */
|
||||
enum {
|
||||
MAX77818_CHGR_INT = 0,
|
||||
MAX77818_FG_INT,
|
||||
MAX77818_SYS_INT,
|
||||
|
||||
MAX77818_SYS_IRQ_START,
|
||||
MAX77818_SYS_IRQ_UVLO = MAX77818_SYS_IRQ_START,
|
||||
MAX77818_SYS_IRQ_OVLO,
|
||||
MAX77818_SYS_IRQ_TSHDN,
|
||||
MAX77818_SYS_IRQ_TM,
|
||||
|
||||
MAX77818_CHG_IRQ_START,
|
||||
MAX77818_CHG_IRQ_BYP_I = MAX77818_CHG_IRQ_START,
|
||||
MAX77818_CHG_IRQ_BATP_I,
|
||||
MAX77818_CHG_IRQ_BAT_I,
|
||||
MAX77818_CHG_IRQ_CHG_I,
|
||||
MAX77818_CHG_IRQ_WCIN_I,
|
||||
MAX77818_CHG_IRQ_CHGIN_I,
|
||||
MAX77818_CHG_IRQ_AICL_I,
|
||||
|
||||
MAX77818_NUM_OF_INTS,
|
||||
};
|
||||
|
||||
enum {
|
||||
SYS_IRQ_UVLO = 0,
|
||||
SYS_IRQ_OVLO,
|
||||
SYS_IRQ_TSHDN,
|
||||
SYS_IRQ_TM,
|
||||
|
||||
CHG_IRQ_BYP_I = 0,
|
||||
CHG_IRQ_BATP_I,
|
||||
CHG_IRQ_BAT_I,
|
||||
CHG_IRQ_CHG_I,
|
||||
CHG_IRQ_WCIN_I,
|
||||
CHG_IRQ_CHGIN_I,
|
||||
CHG_IRQ_AICL_I,
|
||||
|
||||
FG_IRQ_ALERT = 0,
|
||||
};
|
||||
|
||||
|
||||
struct max77818_dev {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
struct regmap_irq_chip_data *irqc_intsrc;
|
||||
struct regmap_irq_chip_data *irqc_sys;
|
||||
struct regmap_irq_chip_data *irqc_chg;
|
||||
|
||||
struct i2c_client *pmic;
|
||||
struct i2c_client *chg;
|
||||
struct i2c_client *fg;
|
||||
|
||||
struct regmap *regmap_pmic;
|
||||
struct regmap *regmap_chg;
|
||||
struct regmap *regmap_fg;
|
||||
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#endif /* !__MAX77818_MFD_H__ */
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Functions to access SY3686A power management chip.
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_SY7636A_H
|
||||
#define __LINUX_MFD_SY7636A_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define SY7636A_REG_OPERATION_MODE_CRL 0x00
|
||||
#define SY7636A_OPERATION_MODE_CRL_VCOMCTL (1 << 6)
|
||||
#define SY7636A_OPERATION_MODE_CRL_ONOFF (1 << 7)
|
||||
#define SY7636A_REG_VCOM_ADJUST_CTRL_L 0x01
|
||||
#define SY7636A_REG_VCOM_ADJUST_CTRL_H 0x02
|
||||
#define SY7636A_REG_VCOM_ADJUST_CTRL_MASK 0x01ff
|
||||
#define SY7636A_REG_VLDO_VOLTAGE_ADJULST_CTRL 0x03
|
||||
#define SY7636A_REG_POWER_ON_DELAY_TIME 0x06
|
||||
#define SY7636A_REG_FAULT_FLAG 0x07
|
||||
#define SY7636A_FAULT_FLAG_PG (1 << 0)
|
||||
#define SY7636A_REG_TERMISTOR_READOUT 0x08
|
||||
|
||||
#define SY7636A_REG_MAX 0x08
|
||||
|
||||
struct sy7636a {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
unsigned int vcom;
|
||||
struct gpio_desc *pgood_gpio;
|
||||
struct mutex reglock;
|
||||
};
|
||||
|
||||
int get_vcom_voltage_mv(struct regmap *regmap);
|
||||
int set_vcom_voltage_mv(struct regmap *regmap, unsigned int vcom);
|
||||
|
||||
#endif /* __LINUX_MFD_SY7636A_H */
|
|
@ -42,6 +42,7 @@
|
|||
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4359 0x4355
|
||||
#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373
|
||||
#define SDIO_DEVICE_ID_CYPRESS_43012 43012
|
||||
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* cyttsp5_core.h
|
||||
* Parade TrueTouch(TM) Standard Product V5 Core Module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_CYTTSP5_CORE_H
|
||||
#define _LINUX_CYTTSP5_CORE_H
|
||||
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#define CYTTSP5_I2C_NAME "cyttsp5_i2c_adapter"
|
||||
#define CYTTSP5_SPI_NAME "cyttsp5_spi_adapter"
|
||||
|
||||
#define CYTTSP5_CORE_NAME "cyttsp5_core"
|
||||
#define CYTTSP5_MT_NAME "cyttsp5_mt"
|
||||
#define CYTTSP5_BTN_NAME "cyttsp5_btn"
|
||||
#define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity"
|
||||
|
||||
#define CY_DRIVER_NAME TTDA
|
||||
#define CY_DRIVER_MAJOR 03
|
||||
#define CY_DRIVER_MINOR 07
|
||||
|
||||
#define CY_DRIVER_REVCTRL 844339
|
||||
|
||||
#define CY_DRIVER_VERSION \
|
||||
__stringify(CY_DRIVER_NAME) \
|
||||
"." __stringify(CY_DRIVER_MAJOR) \
|
||||
"." __stringify(CY_DRIVER_MINOR) \
|
||||
"." __stringify(CY_DRIVER_REVCTRL)
|
||||
|
||||
#define CY_DRIVER_DATE "20150715" /* YYYYMMDD */
|
||||
|
||||
/* abs settings */
|
||||
#define CY_IGNORE_VALUE -1
|
||||
|
||||
enum cyttsp5_core_platform_flags {
|
||||
CY_CORE_FLAG_NONE,
|
||||
CY_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02,
|
||||
CY_CORE_FLAG_RESTORE_PARAMETERS = 0x04,
|
||||
};
|
||||
|
||||
enum cyttsp5_core_platform_easy_wakeup_gesture {
|
||||
CY_CORE_EWG_NONE,
|
||||
CY_CORE_EWG_TAP_TAP,
|
||||
CY_CORE_EWG_TWO_FINGER_SLIDE,
|
||||
CY_CORE_EWG_RESERVED,
|
||||
CY_CORE_EWG_WAKE_ON_INT_FROM_HOST = 0xFF,
|
||||
};
|
||||
|
||||
enum cyttsp5_loader_platform_flags {
|
||||
CY_LOADER_FLAG_NONE,
|
||||
CY_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE,
|
||||
/* Use CONFIG_VER field in TT_CFG to decide TT_CFG update */
|
||||
CY_LOADER_FLAG_CHECK_TTCONFIG_VERSION,
|
||||
CY_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE,
|
||||
};
|
||||
|
||||
struct touch_settings {
|
||||
const uint8_t *data;
|
||||
uint32_t size;
|
||||
uint8_t tag;
|
||||
};
|
||||
|
||||
struct cyttsp5_touch_firmware {
|
||||
const uint8_t *img;
|
||||
uint32_t size;
|
||||
const uint8_t *ver;
|
||||
uint8_t vsize;
|
||||
uint8_t panel_id;
|
||||
};
|
||||
|
||||
struct cyttsp5_touch_config {
|
||||
struct touch_settings *param_regs;
|
||||
struct touch_settings *param_size;
|
||||
const uint8_t *fw_ver;
|
||||
uint8_t fw_vsize;
|
||||
uint8_t panel_id;
|
||||
};
|
||||
|
||||
struct cyttsp5_loader_platform_data {
|
||||
struct cyttsp5_touch_firmware *fw;
|
||||
struct cyttsp5_touch_config *ttconfig;
|
||||
struct cyttsp5_touch_firmware **fws;
|
||||
struct cyttsp5_touch_config **ttconfigs;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
typedef int (*cyttsp5_platform_read) (struct device *dev, void *buf, int size);
|
||||
|
||||
#define CY_TOUCH_SETTINGS_MAX 32
|
||||
|
||||
struct cyttsp5_core_platform_data {
|
||||
int irq_gpio;
|
||||
int rst_gpio;
|
||||
int level_irq_udelay;
|
||||
u16 hid_desc_register;
|
||||
u16 vendor_id;
|
||||
u16 product_id;
|
||||
|
||||
int (*xres)(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev);
|
||||
int (*init)(struct cyttsp5_core_platform_data *pdata,
|
||||
int on, struct device *dev);
|
||||
int (*power)(struct cyttsp5_core_platform_data *pdata,
|
||||
int on, struct device *dev, atomic_t *ignore_irq);
|
||||
int (*detect)(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev, cyttsp5_platform_read read);
|
||||
int (*irq_stat)(struct cyttsp5_core_platform_data *pdata,
|
||||
struct device *dev);
|
||||
struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX];
|
||||
u32 flags;
|
||||
u8 easy_wakeup_gesture;
|
||||
bool fb_blanking_disabled;
|
||||
};
|
||||
|
||||
struct touch_framework {
|
||||
const int16_t *abs;
|
||||
uint8_t size;
|
||||
uint8_t enable_vkeys;
|
||||
} __packed;
|
||||
|
||||
enum cyttsp5_mt_platform_flags {
|
||||
CY_MT_FLAG_NONE,
|
||||
CY_MT_FLAG_HOVER = 0x04,
|
||||
CY_MT_FLAG_FLIP = 0x08,
|
||||
CY_MT_FLAG_INV_X = 0x10,
|
||||
CY_MT_FLAG_INV_Y = 0x20,
|
||||
CY_MT_FLAG_VKEYS = 0x40,
|
||||
CY_MT_FLAG_NO_TOUCH_ON_LO = 0x80,
|
||||
};
|
||||
|
||||
struct cyttsp5_mt_platform_data {
|
||||
struct touch_framework *frmwrk;
|
||||
unsigned short flags;
|
||||
char const *inp_dev_name;
|
||||
int vkeys_x;
|
||||
int vkeys_y;
|
||||
};
|
||||
|
||||
struct cyttsp5_btn_platform_data {
|
||||
char const *inp_dev_name;
|
||||
};
|
||||
|
||||
struct cyttsp5_proximity_platform_data {
|
||||
struct touch_framework *frmwrk;
|
||||
char const *inp_dev_name;
|
||||
};
|
||||
|
||||
struct cyttsp5_platform_data {
|
||||
struct cyttsp5_core_platform_data *core_pdata;
|
||||
struct cyttsp5_mt_platform_data *mt_pdata;
|
||||
struct cyttsp5_btn_platform_data *btn_pdata;
|
||||
struct cyttsp5_proximity_platform_data *prox_pdata;
|
||||
struct cyttsp5_loader_platform_data *loader_pdata;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_CYTTSP5_CORE_H */
|
|
@ -120,11 +120,32 @@ enum max17047_register {
|
|||
MAX17047_QRTbl30 = 0x42,
|
||||
};
|
||||
|
||||
/* Registers specific to max77818 */
|
||||
enum max77818_register {
|
||||
MAX77818_TTF = 0x20,
|
||||
MAX77818_ConvgCfg = 0x49,
|
||||
MAX77818_TALRT_Th2 = 0xB2,
|
||||
MAX77818_TCURVE = 0xB9,
|
||||
MAX77818_Config2 = 0xBB,
|
||||
MAX77818_ChargeState0 = 0xD1,
|
||||
MAX77818_ChargeState1 = 0xD2,
|
||||
MAX77818_ChargeState2 = 0xD3,
|
||||
MAX77818_ChargeState3 = 0xD4,
|
||||
MAX77818_ChargeState4 = 0xD5,
|
||||
MAX77818_ChargeState5 = 0xD6,
|
||||
MAX77818_ChargeState6 = 0xD7,
|
||||
MAX77818_ChargeState7 = 0xD8,
|
||||
MAX77818_JEITA_Volt = 0xD9,
|
||||
MAX77818_JEITA_Curr = 0xDA,
|
||||
MAX77818_SmartChgCfg = 0xDB,
|
||||
};
|
||||
|
||||
enum max170xx_chip_type {
|
||||
MAXIM_DEVICE_TYPE_UNKNOWN = 0,
|
||||
MAXIM_DEVICE_TYPE_MAX17042,
|
||||
MAXIM_DEVICE_TYPE_MAX17047,
|
||||
MAXIM_DEVICE_TYPE_MAX17050,
|
||||
MAXIM_DEVICE_TYPE_MAX77818,
|
||||
|
||||
MAXIM_DEVICE_TYPE_NUM
|
||||
};
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Maxim MAX77818 Battery Utils
|
||||
*
|
||||
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
||||
*
|
||||
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.com>
|
||||
* Author: Shawn Guo <shawn.guo@linaro.org>
|
||||
* Author: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MAX17818_BATTERY_UTILS_H_
|
||||
#define __MAX17818_BATTERY_UTILS_H_
|
||||
|
||||
#include <linux/mfd/max77818/max77818.h>
|
||||
|
||||
/* Exported function required for modules external to the max77818_battery
|
||||
* module to be able to use the MAX77818_DO_NON_FGCC_OP macrov*/
|
||||
int max77818_utils_set_fgcc_mode(struct max77818_dev *max77818_dev,
|
||||
bool enabled,
|
||||
bool *cur_mode);
|
||||
|
||||
/* Magic to enable optional macro param */
|
||||
#define VARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
|
||||
#define VARGS(...) VARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
|
||||
#define CONCAT_(a, b) a##b
|
||||
#define CONCAT(a, b) CONCAT_(a, b)
|
||||
|
||||
/* Common macro to be user from any context having access to the common
|
||||
* max77818 struct defined in the max77818 MDF driver */
|
||||
#define MAX77818_START_NON_FGCC_OP_3(max77818_dev, fgcc_restore_state, op_description) ( \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
bool restore_state = 0; \
|
||||
\
|
||||
if (!max77818_dev) { \
|
||||
printk("%s: max77818_dev is NULL in MAX77818_DO_NON_FGCC_OP\n", __func__); \
|
||||
ret = -EINVAL; \
|
||||
} \
|
||||
else { \
|
||||
dev_dbg(max77818_dev->dev, op_description); \
|
||||
\
|
||||
dev_dbg(max77818_dev->dev, "Applying lock\n"); \
|
||||
mutex_lock(&max77818_dev->lock); \
|
||||
\
|
||||
dev_dbg(max77818_dev->dev, "Clearing FGCC mode\n"); \
|
||||
ret = max77818_utils_set_fgcc_mode(max77818_dev, \
|
||||
false, \
|
||||
&restore_state); \
|
||||
if (ret) { \
|
||||
dev_err(max77818_dev->dev, \
|
||||
"Failed to clear FGCC bit in CONFIG register\n"); \
|
||||
} \
|
||||
else { \
|
||||
fgcc_restore_state = restore_state; \
|
||||
} \
|
||||
\
|
||||
/* UNLOCKING IS DONE IN MAX77818_FINISH_NON_FGCC_OP */ \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
#define MAX77818_START_NON_FGCC_OP_2(max77818_dev, fgcc_restore_state) MAX77818_START_NON_FGCC_OP_3(max77818_dev, fgcc_restore_state, "")
|
||||
#define MAX77818_START_NON_FGCC_OP(...) ( CONCAT(MAX77818_START_NON_FGCC_OP_, VARGS(__VA_ARGS__))(__VA_ARGS__) )
|
||||
|
||||
/* Common macro to be user from any context having access to the common
|
||||
* max77818 struct defined in the max77818 MDF driver */
|
||||
#define MAX77818_FINISH_NON_FGCC_OP_3(max77818_dev, fgcc_restore_state, op_description) ( \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
\
|
||||
if (!max77818_dev) { \
|
||||
printk("%s: max77818_dev is NULL in MAX77818_DO_NON_FGCC_OP\n", __func__); \
|
||||
ret = -EINVAL; \
|
||||
} \
|
||||
else { \
|
||||
dev_dbg(max77818_dev->dev, op_description); \
|
||||
\
|
||||
if (fgcc_restore_state) { \
|
||||
dev_dbg(max77818_dev->dev, "Restoring FGCC mode\n"); \
|
||||
\
|
||||
ret = max77818_utils_set_fgcc_mode(max77818_dev, \
|
||||
true, \
|
||||
NULL); \
|
||||
if (ret) { \
|
||||
dev_err(max77818_dev->dev, \
|
||||
"Failed to set FGCC bit in CONFIG register\n"); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
dev_dbg(max77818_dev->dev, \
|
||||
"Leaving FGCC bit as it were (OFF)\n"); \
|
||||
} \
|
||||
dev_dbg(max77818_dev->dev, "Releasing lock\n"); \
|
||||
mutex_unlock(&max77818_dev->lock); \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
#define MAX77818_FINISH_NON_FGCC_OP_2(max77818_dev, fgcc_restore_state) MAX77818_FINISH_NON_FGCC_OP_3(max77818_dev, fgcc_restore_state, "")
|
||||
#define MAX77818_FINISH_NON_FGCC_OP(...) ( CONCAT(MAX77818_FINISH_NON_FGCC_OP_, VARGS(__VA_ARGS__))(__VA_ARGS__) )
|
||||
|
||||
/* Common macro to be used from any context having access to the common
|
||||
* max77818 struct defined in the max77818 MFD driver */
|
||||
#define MAX77818_DO_NON_FGCC_OP_3(max77818_dev, op, op_description) ( \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
bool restore_state = 0; \
|
||||
\
|
||||
if (!max77818_dev) { \
|
||||
printk("%s: max77818_dev is NULL in MAX77818_DO_NON_FGCC_OP\n", __func__); \
|
||||
ret = -EINVAL; \
|
||||
} \
|
||||
else { \
|
||||
dev_dbg(max77818_dev->dev, "Applying lock\n"); \
|
||||
mutex_lock(&max77818_dev->lock); \
|
||||
\
|
||||
dev_dbg(max77818_dev->dev, "Clearing FGCC mode\n"); \
|
||||
\
|
||||
ret = max77818_utils_set_fgcc_mode(max77818_dev, \
|
||||
false, \
|
||||
&restore_state); \
|
||||
if (ret) { \
|
||||
dev_err(max77818_dev->dev, \
|
||||
"Failed to clear FGCC bit in CONFIG register\n"); \
|
||||
} \
|
||||
else { \
|
||||
dev_dbg(max77818_dev->dev, op_description); \
|
||||
ret = op; \
|
||||
\
|
||||
if (ret) { \
|
||||
dev_err(max77818_dev->dev, \
|
||||
"Failed to read charger mode from charger driver\n"); \
|
||||
} \
|
||||
else { \
|
||||
if (restore_state) { \
|
||||
dev_dbg(max77818_dev->dev, "Restoring FGCC mode\n"); \
|
||||
\
|
||||
ret = max77818_utils_set_fgcc_mode( \
|
||||
max77818_dev, true, NULL); \
|
||||
if (ret) { \
|
||||
dev_err(max77818_dev->dev, \
|
||||
"Failed to set FGCC bit in CONFIG register\n"); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
dev_dbg(max77818_dev->dev, \
|
||||
"Leaving FGCC bit as it were (OFF)\n"); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
dev_dbg(max77818_dev->dev, "Releasing lock\n"); \
|
||||
mutex_unlock(&max77818_dev->lock); \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
#define MAX77818_DO_NON_FGCC_OP_2(max77818_dev, op) MAX77818_DO_NON_FGCC_OP_3(max77818_dev, op, "")
|
||||
#define MAX77818_DO_NON_FGCC_OP(...) ( CONCAT(MAX77818_DO_NON_FGCC_OP_, VARGS(__VA_ARGS__))(__VA_ARGS__) )
|
||||
|
||||
#endif /* __MAX17818_BATTERY_UTILS_H_ */
|
|
@ -88,6 +88,21 @@ enum {
|
|||
POWER_SUPPLY_SCOPE_DEVICE,
|
||||
};
|
||||
|
||||
enum {
|
||||
POWER_SUPPLY_MODE_CHARGER = 0,
|
||||
POWER_SUPPLY_MODE_OTG_SUPPLY,
|
||||
POWER_SUPPLY_MODE_ALL_OFF,
|
||||
};
|
||||
|
||||
enum {
|
||||
POWER_SUPPLY_STATUS_EX_NOT_CONNECTED = 0,
|
||||
POWER_SUPPLY_STATUS_EX_POGO_CONNECTED,
|
||||
POWER_SUPPLY_STATUS_EX_USB_C_CONNECTED,
|
||||
POWER_SUPPLY_STATUS_EX_BOTH_CONNECTED,
|
||||
POWER_SUPPLY_STATUS_EX_CHANGING,
|
||||
POWER_SUPPLY_STATUS_EX_UNKNOWN,
|
||||
};
|
||||
|
||||
enum power_supply_property {
|
||||
/* Properties of type `int' */
|
||||
POWER_SUPPLY_PROP_STATUS = 0,
|
||||
|
@ -107,6 +122,7 @@ enum power_supply_property {
|
|||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_BOOT,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX2,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
POWER_SUPPLY_PROP_CURRENT_BOOT,
|
||||
|
@ -162,6 +178,14 @@ enum power_supply_property {
|
|||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||
|
||||
/* MAX77818 specific mode of operation (OTG supply/charger) */
|
||||
POWER_SUPPLY_PROP_CHARGER_MODE,
|
||||
|
||||
/* MAX77818-charger specific property to get extended charger status indicating
|
||||
* which of the two charger inputs are connected
|
||||
*/
|
||||
POWER_SUPPLY_PROP_STATUS_EX,
|
||||
};
|
||||
|
||||
enum power_supply_type {
|
||||
|
@ -443,6 +467,7 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp)
|
|||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX2:
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
case POWER_SUPPLY_PROP_CURRENT_BOOT:
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* cyttsp5_device_access-api.h
|
||||
* Parade TrueTouch(TM) Standard Product V5 Device Access API module.
|
||||
* For use with Parade touchscreen controllers.
|
||||
* Supported parts include:
|
||||
* CYTMA5XX
|
||||
* CYTMA448
|
||||
* CYTMA445A
|
||||
* CYTT21XXX
|
||||
* CYTT31XXX
|
||||
*
|
||||
* Copyright (C) 2015 Parade Technologies
|
||||
* Copyright (C) 2012-2015 Cypress Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2, and only 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.
|
||||
*
|
||||
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_CYTTSP5_DEVICE_ACCESS_API_H
|
||||
#define _LINUX_CYTTSP5_DEVICE_ACCESS_API_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
int cyttsp5_device_access_user_command(const char *core_name, u16 read_len,
|
||||
u8 *read_buf, u16 write_len, u8 *write_buf,
|
||||
u16 *actual_read_len);
|
||||
|
||||
int cyttsp5_device_access_user_command_async(const char *core_name,
|
||||
u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
|
||||
void (*cont)(const char *core_name, u16 read_len, u8 *read_buf,
|
||||
u16 write_len, u8 *write_buf, u16 actual_read_length,
|
||||
int rc));
|
||||
#endif /* _LINUX_CYTTSP5_DEVICE_ACCESS_API_H */
|
|
@ -194,6 +194,13 @@ config PM_TEST_SUSPEND
|
|||
config PM_SLEEP_DEBUG
|
||||
def_bool y
|
||||
depends on PM_DEBUG && PM_SLEEP
|
||||
|
||||
config PM_RESLEEP_LOCK
|
||||
bool "Lock sleeping x seconds after wakeup"
|
||||
depends on PM
|
||||
---help---
|
||||
This option will let you prevent sleep for x seconds after your machine
|
||||
has woken from sleep.
|
||||
|
||||
config DPM_WATCHDOG
|
||||
bool "Device suspend/resume watchdog"
|
||||
|
|
|
@ -333,6 +333,44 @@ static int suspend_test(int level)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RESLEEP_LOCK
|
||||
struct delayed_work resleep_work;
|
||||
static DEFINE_MUTEX(resleep_mutex);
|
||||
|
||||
static void resleep_unlock(struct work_struct *work)
|
||||
{
|
||||
mutex_unlock(&resleep_mutex);
|
||||
pr_info("Sleep re-enabled\n");
|
||||
}
|
||||
#endif /* CONFIG_PM_RESLEEP_LOCK */
|
||||
|
||||
static void resleep_timer(suspend_state_t state)
|
||||
{
|
||||
#ifdef CONFIG_PM_RESLEEP_LOCK
|
||||
if (state != PM_SUSPEND_MEM)
|
||||
return;
|
||||
|
||||
INIT_DELAYED_WORK(&resleep_work, &resleep_unlock);
|
||||
if (!schedule_delayed_work(&resleep_work, msecs_to_jiffies(10*1000))) {
|
||||
pr_warning("Failed to schedule delayed work, unlocking mutex\n");
|
||||
mutex_unlock(&resleep_mutex);
|
||||
}
|
||||
#endif /* CONFIG_PM_RESLEEP_LOCK */
|
||||
}
|
||||
|
||||
static bool resleep_isok(suspend_state_t state)
|
||||
{
|
||||
#ifdef CONFIG_PM_RESLEEP_LOCK
|
||||
if (state != PM_SUSPEND_MEM)
|
||||
return true;
|
||||
|
||||
if (!mutex_trylock(&resleep_mutex))
|
||||
return false;
|
||||
#endif /* CONFIG_PM_RESLEEP_LOCK */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* suspend_prepare - Prepare for entering system sleep state.
|
||||
*
|
||||
|
@ -347,6 +385,9 @@ static int suspend_prepare(suspend_state_t state)
|
|||
if (!sleep_state_supported(state))
|
||||
return -EPERM;
|
||||
|
||||
if (!resleep_isok(state))
|
||||
return -EBUSY;
|
||||
|
||||
pm_prepare_console();
|
||||
|
||||
error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
|
||||
|
@ -529,11 +570,13 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|||
* Call platform code to clean up, restart processes, and free the console that
|
||||
* we've allocated. This routine is not called for hibernation.
|
||||
*/
|
||||
static void suspend_finish(void)
|
||||
static void suspend_finish(suspend_state_t state)
|
||||
{
|
||||
suspend_thaw_processes();
|
||||
pm_notifier_call_chain(PM_POST_SUSPEND);
|
||||
pm_restore_console();
|
||||
|
||||
resleep_timer(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -589,7 +632,7 @@ static int enter_state(suspend_state_t state)
|
|||
Finish:
|
||||
events_check_enabled = false;
|
||||
pm_pr_dbg("Finishing wakeup.\n");
|
||||
suspend_finish();
|
||||
suspend_finish(state);
|
||||
Unlock:
|
||||
mutex_unlock(&system_transition_mutex);
|
||||
return error;
|
||||
|
|
Loading…
Reference in New Issue