1
0
Fork 0

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
Alistair Francis 2021-01-30 10:28:01 -08:00
parent 739c2edfa2
commit 2abfd510f9
87 changed files with 33567 additions and 172 deletions

View File

@ -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 \

View File

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

View File

@ -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 = <&reg_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 = <&reg_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 = <&reg_epdpmic>;
lcd2-supply = <&reg_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 = <&reg_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
>;
};
};

View File

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

View File

@ -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(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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, &reg_val);
if (ret)
return ret;
temp = *((signed char*)&reg_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");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,
},
};

View File

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

View File

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

View File

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

View File

@ -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 *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"

View File

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

View File

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

View File

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

View File

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

View File

@ -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, &reg_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*)&reg_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");

View File

@ -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)
{
/*

View File

@ -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,

View File

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

View File

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

View File

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

View File

@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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:

View File

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

View File

@ -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"

View File

@ -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;