Drivers/PMIC: support pmic regulator function for hikey970.
Add PMIC code in kernel to support pmic regulator for hikey970. Signed-off-by: mwx533604 <mwx533604@notesmail.huawei.com>
This commit is contained in:
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* publishhed by the Free Software Foundation.
|
||||
*/
|
||||
/*hisilicon,valid-modes-mask:
|
||||
*#define REGULATOR_MODE_FAST 0x1
|
||||
*#define REGULATOR_MODE_NORMAL 0x2
|
||||
*#define REGULATOR_MODE_IDLE 0x4
|
||||
*#define REGULATOR_MODE_STANDBY 0x8
|
||||
*/
|
||||
/*hisilicon,valid-idle-mask:
|
||||
*#define REGULATOR_CHANGE_VOLTAGE 0x1
|
||||
*#define REGULATOR_CHANGE_CURRENT 0x2
|
||||
*#define REGULATOR_CHANGE_MODE 0x4
|
||||
*#define REGULATOR_CHANGE_STATUS 0x8
|
||||
*#define REGULATOR_CHANGE_DRMS 0x10
|
||||
*#define REGULATOR_CHANGE_BYPASS 0x20
|
||||
*/
|
||||
/ {
|
||||
spmi: spmi@FFF24000 {
|
||||
pmic: pmic@0 {
|
||||
compatible = "hisilicon-hisi-pmic-spmi";
|
||||
slave_id = <0>;
|
||||
reg = <0 0>;
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
gpios = <&gpio28 0 0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <>;
|
||||
hisilicon,hisi-pmic-irq-num = <16>;/*hisi irq number*/
|
||||
hisilicon,hisi-pmic-irq-array = <2>;/*hisi irq array*/
|
||||
hisilicon,hisi-pmic-irq-mask-addr = <0x202 2>;/*hisi irq mask addr*/
|
||||
hisilicon,hisi-pmic-irq-addr = <0x212 2>;/*hisi irq addr*/
|
||||
hisilicon,pmic_fpga_flag=<0>;
|
||||
hisilicon,hisi-pmic-vbus = <0x6 1>;/*vbus status addr and bit*/
|
||||
hisilicon,hisi-pmic-lock = <0x125 0x7D>; /*lock: <addr value>*/
|
||||
hisilicon,hisi-pmic-debug-lock = <0x244 0x6C>;/*debug-lock: <addr value>*/
|
||||
spmi-slave-container;/*spmi-dev-container;*/
|
||||
};
|
||||
/* need to modify according to the design */
|
||||
ldo3: ldo3@16 {
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <9 0>;
|
||||
regulator-name = "ldo3";
|
||||
regulator-min-microvolt = <1500000>;
|
||||
regulator-max-microvolt = <2000000>;
|
||||
hisilicon,hisi-ctrl = <0x16 0x01 0x00>;/*ctrl_reg、enable_mask、eco_mode_mask*/
|
||||
hisilicon,hisi-vset = <0x51 0x0F>;
|
||||
hisilicon,hisi-n-voltages = <16>;
|
||||
hisilicon,hisi-vset-table = <1500000>, <1550000>,
|
||||
<1600000>, <1650000>,
|
||||
<1700000>, <1725000>,
|
||||
<1750000>, <1775000>,
|
||||
<1800000>, <1825000>,
|
||||
<1850000>, <1875000>,
|
||||
<1900000>, <1925000>,
|
||||
<1950000>, <2000000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <120>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x02>;
|
||||
hisilicon,valid-idle-mask = <0x09>;
|
||||
};
|
||||
|
||||
ldo4: ldo4@17 { /* 40 PIN */
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <1 0>;
|
||||
regulator-name = "ldo4";
|
||||
regulator-min-microvolt = <1725000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
hisilicon,hisi-ctrl = <0x17 0x01 0x10>;/*ctrl_reg、enable_mask、eco_mode_mask*/
|
||||
hisilicon,hisi-vset = <0x52 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <1725000>, <1750000>,
|
||||
<1775000>, <1800000>,
|
||||
<1825000>, <1850000>,
|
||||
<1875000>, <1900000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <120>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x06>;
|
||||
hisilicon,valid-idle-mask = <0x0D>;
|
||||
};
|
||||
|
||||
ldo9: ldo9@1C { /* SDCARD I/O */
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <2 0>;
|
||||
regulator-name = "ldo9";
|
||||
regulator-min-microvolt = <1750000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
hisilicon,hisi-ctrl = <0x1C 0x01 0x10>;
|
||||
hisilicon,hisi-vset = <0x57 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <1750000>, <1800000>,
|
||||
<1825000>, <2800000>,
|
||||
<2850000>, <2950000>,
|
||||
<3000000>, <3300000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <360>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x06>;
|
||||
hisilicon,valid-idle-mask = <0x0D>;
|
||||
};
|
||||
|
||||
ldo15: ldo15@21 { /* UFS */
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <3 0>;
|
||||
regulator-name = "ldo15";
|
||||
regulator-always-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
hisilicon,hisi-ctrl = <0x21 0x01 0x10>;
|
||||
hisilicon,hisi-vset = <0x5C 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <1800000>, <1850000>,
|
||||
<2400000>, <2600000>,
|
||||
<2700000>, <2850000>,
|
||||
<2950000>, <3000000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <120>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x06>;
|
||||
hisilicon,valid-idle-mask = <0x0D>;
|
||||
};
|
||||
|
||||
ldo16: ldo16@22 { /* SD */
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <4 0>;
|
||||
regulator-name = "ldo16";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
hisilicon,hisi-ctrl = <0x22 0x01 0x10>;
|
||||
hisilicon,hisi-vset = <0x5D 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <1800000>, <1850000>,
|
||||
<2400000>, <2600000>,
|
||||
<2700000>, <2850000>,
|
||||
<2950000>, <3000000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <360>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x06>;
|
||||
hisilicon,valid-idle-mask = <0x0D>;
|
||||
};
|
||||
|
||||
ldo17: ldo17@23 {
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <7 0>;
|
||||
regulator-name = "ldo17";
|
||||
regulator-min-microvolt = <2500000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
hisilicon,hisi-ctrl = <0x23 0x01 0x10>;
|
||||
hisilicon,hisi-vset = <0x5E 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <2500000>, <2600000>,
|
||||
<2700000>, <2800000>,
|
||||
<3000000>, <3100000>,
|
||||
<3200000>, <3300000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <120>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x06>;
|
||||
hisilicon,valid-idle-mask = <0x0D>;
|
||||
};
|
||||
|
||||
ldo33: ldo33@32 { /* PEX8606 */
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <5 0>;
|
||||
regulator-name = "ldo33";
|
||||
regulator-min-microvolt = <2500000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
hisilicon,hisi-ctrl = <0x32 0x01 0x00>;
|
||||
hisilicon,hisi-vset = <0x6D 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <2500000>, <2600000>,
|
||||
<2700000>, <2800000>,
|
||||
<3000000>, <3100000>,
|
||||
<3200000>, <3300000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <120>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x02>;
|
||||
hisilicon,valid-idle-mask = <0x09>;
|
||||
};
|
||||
|
||||
ldo34: ldo34@33 { /* GPS AUX IN VDD*/
|
||||
compatible = "hisilicon-hisi-ldo";
|
||||
reg = <6 0>;
|
||||
regulator-name = "ldo34";
|
||||
regulator-min-microvolt = <2600000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
hisilicon,hisi-ctrl = <0x33 0x01 0x00>;
|
||||
hisilicon,hisi-vset = <0x6E 0x07>;
|
||||
hisilicon,hisi-n-voltages = <8>;
|
||||
hisilicon,hisi-vset-table = <2600000>, <2700000>,
|
||||
<2800000>, <2900000>,
|
||||
<3000000>, <3100000>,
|
||||
<3200000>, <3300000>;
|
||||
hisilicon,hisi-off-on-delay-us = <20000>;
|
||||
hisilicon,hisi-enable-time-us = <120>;
|
||||
hisilicon,hisi-eco-microamp = <10000>;
|
||||
hisilicon,valid-modes-mask = <0x02>;
|
||||
hisilicon,valid-idle-mask = <0x09>;
|
||||
};
|
||||
};/*end of spmi*/
|
||||
};
|
||||
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dts file for Hisilicon HiKey970 Development Board
|
||||
*
|
||||
* Copyright (C) 2016, Hisilicon Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
#include "kirin970.dtsi"
|
||||
#include "kirin970-gpu.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include "kirin970_spmi.dtsi"
|
||||
#include "hisi_6421v600_pmic_spmi.dtsi"
|
||||
#include "kirin970-drm.dtsi"
|
||||
#include "kirin970-ion.dtsi"
|
||||
#include "kirin970_ipc.dtsi"
|
||||
/ {
|
||||
model = "HiKey970";
|
||||
compatible = "Hisilicon,kirin970-hikey970", "Hisilicon,kirin970";
|
||||
|
||||
aliases {
|
||||
mshc1 = &dwmmc1;
|
||||
mshc2 = &dwmmc2;
|
||||
serial0 = &uart0; /* UDP console UART */
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
serial4 = &uart4;
|
||||
serial5 = &uart5;
|
||||
serial6 = &uart6;
|
||||
};
|
||||
|
||||
chosen {
|
||||
stdout-path = "serial6:115200n8";
|
||||
};
|
||||
|
||||
memory@0 {
|
||||
device_type = "memory";
|
||||
/* rewrite this at bootloader */
|
||||
reg = <0x0 0x0 0x0 0x0>;
|
||||
};
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
bl31 {
|
||||
reg = <0x0 0x1FE00000 0x0 0x400000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
ramoops_ram:pstore-mem{
|
||||
reg = <0x0 0x20A00000 0x0 0x100000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
uefi-reboot-mode {
|
||||
reg = <0x0 0x32100000 0x0 0x00001000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
hifi-base {
|
||||
reg = <0x0 0x8D500000 0x0 0xC00000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
lpmx-core {
|
||||
reg = <0x0 0x8E100000 0x0 0x100000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
lpmcu {
|
||||
reg = <0x0 0x8E200000 0x0 0x40000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
hifi-data {
|
||||
reg = <0x0 0x8E300000 0x0 0x500000>;
|
||||
no-map;
|
||||
};
|
||||
};
|
||||
|
||||
reboot-mode-syscon@32100000 {
|
||||
compatible = "syscon", "simple-mfd";
|
||||
reg = <0x0 0x32100000 0x0 0x00001000>;
|
||||
|
||||
reboot-mode {
|
||||
compatible = "syscon-reboot-mode";
|
||||
offset = <0x0>;
|
||||
|
||||
mode-normal = <0x77665501>;
|
||||
mode-bootloader = <0x77665500>;
|
||||
mode-recovery = <0x77665502>;
|
||||
};
|
||||
};
|
||||
pclk: apb_pclk {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <20000000>;
|
||||
clock-output-names = "apb_pclk";
|
||||
};
|
||||
|
||||
keys: gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pwr_key_pmx_func &pwr_key_cfg_func>;
|
||||
|
||||
power {
|
||||
wakeup-source;
|
||||
gpios = <&gpio25 3 GPIO_ACTIVE_LOW>;
|
||||
label = "GPIO Power";
|
||||
linux,code = <KEY_POWER>;
|
||||
};
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
can_active_led {
|
||||
label = "can_active";
|
||||
/* GPIO_012_USER_LED5*/
|
||||
gpios = <&gpio1 4 0>;
|
||||
linux,default-trigger = "can0-rxtx";
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
|
||||
soc {
|
||||
uart6: serial@fff32000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
hifidsp {
|
||||
compatible = "hisilicon,k3hifidsp";
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
user_led2 {
|
||||
label = "user_led2";
|
||||
/* gpio_151_user_led2 */
|
||||
gpios = <&gpio20 7 0>;
|
||||
linux,default-trigger = "mmc0";
|
||||
};
|
||||
|
||||
wlan_active_led {
|
||||
label = "wifi_active";
|
||||
/* gpio_024_wifi_active */
|
||||
gpios = <&gpio3 0 0>;
|
||||
linux,default-trigger = "phy0tx";
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
|
||||
vdec {
|
||||
compatible = "hisi,kirin970-vdec";
|
||||
reg = <0x0 0xe8800000 0x0 0x40000>;
|
||||
interrupts = <0 290 4>, <0 291 4>, <0 292 4>;
|
||||
clocks = <&media2_crg KIRIN970_CLK_GATE_VDECFREQ>;
|
||||
clock-names = "clk_gate_vdecfreq";
|
||||
/*clocks = <&clk_gate_vdec>;*/
|
||||
/*clock-names = "clk_vdec";*/
|
||||
dec_clk_rate = <450000000>, <300000000>, <185000000>;
|
||||
vdec_fpga = <0x1>;
|
||||
status = "ok"
|
||||
iommu_info {
|
||||
start-addr = <0x40000>;
|
||||
size = <0xE0000000>;
|
||||
};
|
||||
};
|
||||
|
||||
venc {
|
||||
compatible = "hisi,kirin970-venc";
|
||||
reg = <0x0 0xe8880000 0x0 0x40000>;
|
||||
interrupts = <0 296 4>, <0 297 4>, <0 298 4>;
|
||||
clocks = <&media2_crg KIRIN970_CLK_GATE_VENCFREQ>;
|
||||
clock-names = "clk_gate_vencfreq";
|
||||
/*clocks = <&clk_gate_venc &venc_volt_hold>;*/
|
||||
/*clock-names = "clk_venc", "venc_volt_hold";*/
|
||||
enc_clk_rate = <554000000>, <450000000>, <238000000>;
|
||||
venc_fpga = <0x1>;
|
||||
status = "ok"
|
||||
iommu_info {
|
||||
start-addr = <0x40000>;
|
||||
size = <0xE0000000>;
|
||||
};
|
||||
};
|
||||
|
||||
smmu_lpae{
|
||||
compatible = "hisi,hisi-smmu-lpae";
|
||||
status = "ok"
|
||||
};
|
||||
|
||||
wlan_en: wlan-en-1-8v {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "wlan-en-regulator";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
|
||||
/* GPIO_051_WIFI_EN */
|
||||
gpio = <&gpio6 3 0>;
|
||||
regulator-always-on;
|
||||
|
||||
/* WLAN card specific delay */
|
||||
startup-delay-us = <70000>;
|
||||
enable-active-high;
|
||||
};
|
||||
};
|
||||
|
||||
&dwmmc1 {
|
||||
vmmc-supply = <&ldo16>;
|
||||
vqmmc-supply = <&ldo9>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&dwmmc2 { /* WIFI */
|
||||
broken-cd;
|
||||
/* WL_EN */
|
||||
vmmc-supply = <&wlan_en>;
|
||||
ti,non-removable;
|
||||
non-removable;
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
status = "ok"
|
||||
|
||||
wlcore: wlcore@2 {
|
||||
compatible = "ti,wl1837";
|
||||
reg = <2>; /* sdio func num */
|
||||
/* WL_IRQ, GPIO_177_WL_WAKEUP_AP */
|
||||
interrupt-parent = <&gpio22>;
|
||||
interrupts = <1 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Hisilicon Ltd. kirin970 SoC
|
||||
*
|
||||
* Copyright (C) 2014-2016 Hisilicon Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* publishhed by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/{
|
||||
spmi: spmi@FFF24000 {
|
||||
compatible = "hisilicon,spmi-controller";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
status = "ok"
|
||||
reg = <0x0 0xfff24000 0x0 0x1000>;
|
||||
spmi-channel = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
Regular → Executable
+1
-1
@@ -448,4 +448,4 @@ static int __init pl061_gpio_init(void)
|
||||
{
|
||||
return amba_driver_register(&pl061_gpio_driver);
|
||||
}
|
||||
device_initcall(pl061_gpio_init);
|
||||
subsys_initcall(pl061_gpio_init);
|
||||
|
||||
Regular → Executable
+10
@@ -358,6 +358,16 @@ config MFD_HI655X_PMIC
|
||||
help
|
||||
Select this option to enable Hisilicon hi655x series pmic driver.
|
||||
|
||||
config HISI_PMIC_PMU_SPMI
|
||||
tristate "HiSilicon PMU/Codec IC and use spmi passageway"
|
||||
depends on OF
|
||||
depends on HISI_SPMI
|
||||
help
|
||||
This driver supports HiSilicon Hisi power management and codec IC,
|
||||
including regulators, codec, ADCs, Coulomb counter, etc. Memory
|
||||
mapped I/O ports are the way of communication with it. and use spmi
|
||||
passageway read pmu address.
|
||||
|
||||
config HTC_PASIC3
|
||||
tristate "HTC PASIC3 LED/DS1WM chip support"
|
||||
select MFD_CORE
|
||||
|
||||
Regular → Executable
+1
@@ -201,6 +201,7 @@ obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
|
||||
obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o
|
||||
obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
|
||||
obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
|
||||
obj-$(CONFIG_HISI_PMIC_PMU_SPMI) += hisi_pmic_spmi.o
|
||||
obj-$(CONFIG_MFD_DLN2) += dln2.o
|
||||
obj-$(CONFIG_MFD_RT5033) += rt5033.o
|
||||
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
|
||||
|
||||
Executable
+759
@@ -0,0 +1,759 @@
|
||||
/*
|
||||
* Device driver for regulators in HISI PMIC IC
|
||||
*
|
||||
* Copyright (c) 2013 Linaro Ltd.
|
||||
* Copyright (c) 2011 Hisilicon.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/mfd/hisi_pmic.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spmi.h>
|
||||
#ifndef NO_IRQ
|
||||
#define NO_IRQ 0
|
||||
#endif
|
||||
|
||||
/* 8-bit register offset in PMIC */
|
||||
#define HISI_MASK_STATE 0xff
|
||||
|
||||
#define HISI_IRQ_KEY_NUM 0
|
||||
#define HISI_IRQ_KEY_VALUE 0xc0
|
||||
#define HISI_IRQ_KEY_DOWN 7
|
||||
#define HISI_IRQ_KEY_UP 6
|
||||
|
||||
/*#define HISI_NR_IRQ 25*/
|
||||
#define HISI_MASK_FIELD 0xFF
|
||||
#define HISI_BITS 8
|
||||
#define PMIC_FPGA_FLAG 1
|
||||
|
||||
/*define the first group interrupt register number*/
|
||||
#define HISI_PMIC_FIRST_GROUP_INT_NUM 2
|
||||
|
||||
static struct bit_info g_pmic_vbus = {0};
|
||||
#ifndef BIT
|
||||
#define BIT(x) (0x1U << (x))
|
||||
#endif
|
||||
|
||||
static struct hisi_pmic *g_pmic;
|
||||
static unsigned int g_extinterrupt_flag = 0;
|
||||
static struct of_device_id of_hisi_pmic_match_tbl[] = {
|
||||
{
|
||||
.compatible = "hisilicon-hisi-pmic-spmi",
|
||||
},
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
/*
|
||||
* The PMIC register is only 8-bit.
|
||||
* Hisilicon SoC use hardware to map PMIC register into SoC mapping.
|
||||
* At here, we are accessing SoC register with 32-bit.
|
||||
*/
|
||||
u32 hisi_pmic_read(struct hisi_pmic *pmic, int reg)
|
||||
{
|
||||
u32 ret;
|
||||
u8 read_value = 0;
|
||||
struct spmi_device *pdev;
|
||||
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pdev = to_spmi_device(g_pmic->dev);
|
||||
if (NULL == pdev) {
|
||||
pr_err("%s:pdev get failed!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = spmi_ext_register_readl(pdev, reg, (unsigned char*)&read_value, 1);/*lint !e734 !e732 */
|
||||
if (ret) {
|
||||
pr_err("%s:spmi_ext_register_readl failed!\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
return (u32)read_value;
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_read);
|
||||
|
||||
void hisi_pmic_write(struct hisi_pmic *pmic, int reg, u32 val)
|
||||
{
|
||||
u32 ret;
|
||||
struct spmi_device *pdev;
|
||||
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pdev = to_spmi_device(g_pmic->dev);
|
||||
if (NULL == pdev) {
|
||||
pr_err("%s:pdev get failed!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = spmi_ext_register_writel(pdev, reg, (unsigned char*)&val, 1);/*lint !e734 !e732 */
|
||||
if (ret) {
|
||||
pr_err("%s:spmi_ext_register_writel failed!\n", __func__);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_write);
|
||||
|
||||
#ifdef CONFIG_HISI_DIEID
|
||||
u32 hisi_pmic_read_sub_pmu(u8 sid, int reg)
|
||||
{
|
||||
u32 ret;
|
||||
u8 read_value = 0;
|
||||
struct spmi_device *pdev;
|
||||
|
||||
if(strstr(saved_command_line, "androidboot.swtype=factory"))
|
||||
{
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return -1;/*lint !e570 */
|
||||
}
|
||||
|
||||
pdev = to_spmi_device(g_pmic->dev);
|
||||
if (NULL == pdev) {
|
||||
pr_err("%s:pdev get failed!\n", __func__);
|
||||
return -1;/*lint !e570 */
|
||||
}
|
||||
|
||||
ret = spmi_ext_register_readl(pdev->ctrl, sid, reg, (unsigned char*)&read_value, 1);/*lint !e734 !e732 */
|
||||
if (ret) {
|
||||
pr_err("%s:spmi_ext_register_readl failed!\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
return (u32)read_value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_read_sub_pmu);
|
||||
|
||||
void hisi_pmic_write_sub_pmu(u8 sid, int reg, u32 val)
|
||||
{
|
||||
u32 ret;
|
||||
struct spmi_device *pdev;
|
||||
if(strstr(saved_command_line, "androidboot.swtype=factory"))
|
||||
{
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pdev = to_spmi_device(g_pmic->dev);
|
||||
if (NULL == pdev) {
|
||||
pr_err("%s:pdev get failed!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = spmi_ext_register_writel(pdev->ctrl, sid, reg, (unsigned char*)&val, 1);/*lint !e734 !e732 */
|
||||
if (ret) {
|
||||
pr_err("%s:spmi_ext_register_writel failed!\n", __func__);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_write_sub_pmu);
|
||||
#endif
|
||||
|
||||
void hisi_pmic_rmw(struct hisi_pmic *pmic, int reg,
|
||||
u32 mask, u32 bits)
|
||||
{
|
||||
u32 data;
|
||||
unsigned long flags;
|
||||
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&g_pmic->lock, flags);
|
||||
data = hisi_pmic_read(pmic, reg) & ~mask;
|
||||
data |= mask & bits;
|
||||
hisi_pmic_write(pmic, reg, data);
|
||||
spin_unlock_irqrestore(&g_pmic->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_rmw);
|
||||
|
||||
unsigned int hisi_pmic_reg_read(int addr)
|
||||
{
|
||||
return (unsigned int)hisi_pmic_read(g_pmic, addr);
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_reg_read);
|
||||
|
||||
void hisi_pmic_reg_write(int addr, int val)
|
||||
{
|
||||
hisi_pmic_write(g_pmic, addr, val);
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_reg_write);
|
||||
|
||||
void hisi_pmic_reg_write_lock(int addr, int val)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&g_pmic->lock, flags);
|
||||
hisi_pmic_write(g_pmic, g_pmic->normal_lock.addr, g_pmic->normal_lock.val);
|
||||
hisi_pmic_write(g_pmic, g_pmic->debug_lock.addr, g_pmic->debug_lock.val);
|
||||
hisi_pmic_write(g_pmic, addr, val);
|
||||
hisi_pmic_write(g_pmic, g_pmic->normal_lock.addr, 0);
|
||||
hisi_pmic_write(g_pmic, g_pmic->debug_lock.addr, 0);
|
||||
spin_unlock_irqrestore(&g_pmic->lock, flags);
|
||||
}
|
||||
|
||||
int hisi_pmic_array_read(int addr, char *buff, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if ((len > 32) || (NULL == buff)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Here is a bug in the pmu die.
|
||||
* the coul driver will read 4 bytes,
|
||||
* but the ssi bus only read 1 byte, and the pmu die
|
||||
* will make sampling 1/10669us about vol cur,so the driver
|
||||
* read the data is not the same sampling
|
||||
*/
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
*(buff + i) = hisi_pmic_reg_read(addr+i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hisi_pmic_array_write(int addr, char *buff, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if ((len > 32) || (NULL == buff)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
hisi_pmic_reg_write(addr+i, *(buff + i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t hisi_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct hisi_pmic *pmic = (struct hisi_pmic *)data;
|
||||
unsigned long pending;
|
||||
int i, offset;
|
||||
|
||||
for (i = 0; i < pmic->irqarray; i++) {
|
||||
pending = hisi_pmic_reg_read((i + pmic->irq_addr.start_addr));
|
||||
pending &= HISI_MASK_FIELD;
|
||||
if (pending != 0) {
|
||||
pr_info("pending[%d]=0x%lx\n\r", i, pending);
|
||||
}
|
||||
|
||||
hisi_pmic_reg_write((i + pmic->irq_addr.start_addr), pending);
|
||||
|
||||
/*solve powerkey order*/
|
||||
if ((HISI_IRQ_KEY_NUM == i) && ((pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE)) {
|
||||
generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_DOWN]);
|
||||
generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_UP]);
|
||||
pending &= (~HISI_IRQ_KEY_VALUE);
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
for_each_set_bit(offset, &pending, HISI_BITS)
|
||||
generic_handle_irq(pmic->irqs[offset + i * HISI_BITS]);/*lint !e679 */
|
||||
}
|
||||
}
|
||||
|
||||
/*Handle the second group irq if analysis the second group irq from dtsi*/
|
||||
if (1 == g_extinterrupt_flag){
|
||||
for (i = 0; i < pmic->irqarray1; i++) {
|
||||
pending = hisi_pmic_reg_read((i + pmic->irq_addr1.start_addr));
|
||||
pending &= HISI_MASK_FIELD;
|
||||
if (pending != 0) {
|
||||
pr_info("pending[%d]=0x%lx\n\r", i, pending);
|
||||
}
|
||||
|
||||
hisi_pmic_reg_write((i + pmic->irq_addr1.start_addr), pending);
|
||||
|
||||
if (pending) {
|
||||
for_each_set_bit(offset, &pending, HISI_BITS)
|
||||
generic_handle_irq(pmic->irqs[offset + (i+HISI_PMIC_FIRST_GROUP_INT_NUM) * HISI_BITS]);/*lint !e679 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void hisi_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct hisi_pmic *pmic = irq_data_get_irq_chip_data(d);
|
||||
u32 data, offset;
|
||||
unsigned long flags;
|
||||
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
offset = (irqd_to_hwirq(d) >> 3);
|
||||
if (1==g_extinterrupt_flag){
|
||||
if ( offset < HISI_PMIC_FIRST_GROUP_INT_NUM)
|
||||
offset += pmic->irq_mask_addr.start_addr;
|
||||
else/*Change addr when irq num larger than 16 because interrupt addr is nonsequence*/
|
||||
offset = offset+(pmic->irq_mask_addr1.start_addr)-HISI_PMIC_FIRST_GROUP_INT_NUM;
|
||||
}else{
|
||||
offset += pmic->irq_mask_addr.start_addr;
|
||||
}
|
||||
spin_lock_irqsave(&g_pmic->lock, flags);
|
||||
data = hisi_pmic_reg_read(offset);
|
||||
data |= (1 << (irqd_to_hwirq(d) & 0x07));
|
||||
hisi_pmic_reg_write(offset, data);
|
||||
spin_unlock_irqrestore(&g_pmic->lock, flags);
|
||||
}
|
||||
|
||||
static void hisi_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct hisi_pmic *pmic = irq_data_get_irq_chip_data(d);
|
||||
u32 data, offset;
|
||||
unsigned long flags;
|
||||
|
||||
if (NULL == g_pmic) {
|
||||
pr_err(" g_pmic is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
offset = (irqd_to_hwirq(d) >> 3);
|
||||
if (1==g_extinterrupt_flag){
|
||||
if ( offset < HISI_PMIC_FIRST_GROUP_INT_NUM)
|
||||
offset += pmic->irq_mask_addr.start_addr;
|
||||
else
|
||||
offset = offset+(pmic->irq_mask_addr1.start_addr)-HISI_PMIC_FIRST_GROUP_INT_NUM;
|
||||
}else{
|
||||
offset += pmic->irq_mask_addr.start_addr;
|
||||
}
|
||||
spin_lock_irqsave(&g_pmic->lock, flags);
|
||||
data = hisi_pmic_reg_read(offset);
|
||||
data &= ~(1 << (irqd_to_hwirq(d) & 0x07)); /*lint !e502 */
|
||||
hisi_pmic_reg_write(offset, data);
|
||||
spin_unlock_irqrestore(&g_pmic->lock, flags);
|
||||
}
|
||||
|
||||
static struct irq_chip hisi_pmu_irqchip = {
|
||||
.name = "hisi-irq",
|
||||
.irq_mask = hisi_irq_mask,
|
||||
.irq_unmask = hisi_irq_unmask,
|
||||
.irq_disable = hisi_irq_mask,
|
||||
.irq_enable = hisi_irq_unmask,
|
||||
};
|
||||
|
||||
static int hisi_irq_map(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct hisi_pmic *pmic = d->host_data;
|
||||
|
||||
irq_set_chip_and_handler_name(virq, &hisi_pmu_irqchip,
|
||||
handle_simple_irq, "hisi");
|
||||
irq_set_chip_data(virq, pmic);
|
||||
irq_set_irq_type(virq, IRQ_TYPE_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops hisi_domain_ops = {
|
||||
.map = hisi_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
/*lint -e570 -e64*/
|
||||
static int get_pmic_device_tree_data(struct device_node *np, struct hisi_pmic *pmic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*get pmic irq num*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-num",
|
||||
&(pmic->irqnum), 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-num property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*get pmic irq array number*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-array",
|
||||
&(pmic->irqarray), 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-array property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*SOC_PMIC_IRQ_MASK_0_ADDR*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-mask-addr",
|
||||
(int *)&pmic->irq_mask_addr, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-mask-addr property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*SOC_PMIC_IRQ0_ADDR*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-addr",
|
||||
(int *)&pmic->irq_addr, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-addr property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-vbus",
|
||||
(u32 *)&g_pmic_vbus, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-vbus property\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*pmic lock*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-lock",
|
||||
(int *)&pmic->normal_lock, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-lock property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*pmic debug lock*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-debug-lock",
|
||||
(int *)&pmic->debug_lock, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-debug-lock property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}/*lint -restore*/
|
||||
|
||||
|
||||
/*lint -e570 -e64*/
|
||||
static int get_pmic_device_tree_data1(struct device_node *np, struct hisi_pmic *pmic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*get pmic irq num*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-num1",
|
||||
&(pmic->irqnum1), 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-num1 property set\n");
|
||||
ret = -ENODEV;
|
||||
pmic->irqnum1 = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*get pmic irq array number*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-array1",
|
||||
&(pmic->irqarray1), 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-array1 property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*SOC_PMIC_IRQ_MASK_0_ADDR*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-mask-addr1",
|
||||
(int *)&pmic->irq_mask_addr1, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-mask-addr1 property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*SOC_PMIC_IRQ0_ADDR*/
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-addr1",
|
||||
(int *)&pmic->irq_addr1, 2);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,hisi-pmic-irq-addr1 property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
g_extinterrupt_flag = 1;
|
||||
return ret;
|
||||
}/*lint -restore*/
|
||||
|
||||
int hisi_get_pmic_irq_byname(unsigned int pmic_irq_list)
|
||||
{
|
||||
if ( NULL == g_pmic ) {
|
||||
pr_err("[%s]g_pmic is NULL\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pmic_irq_list > (unsigned int)g_pmic->irqnum) {
|
||||
pr_err("[%s]input pmic irq number is error.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
pr_info("%s:g_pmic->irqs[%d]=%d\n", __func__, pmic_irq_list, g_pmic->irqs[pmic_irq_list]);
|
||||
return (int)g_pmic->irqs[pmic_irq_list];
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_get_pmic_irq_byname);
|
||||
|
||||
int hisi_pmic_get_vbus_status(void)
|
||||
{
|
||||
if (0 == g_pmic_vbus.addr)
|
||||
return -1;
|
||||
|
||||
if (hisi_pmic_reg_read(g_pmic_vbus.addr) & BIT(g_pmic_vbus.bit))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_pmic_get_vbus_status);
|
||||
|
||||
static void hisi_pmic_irq_prc(struct hisi_pmic *pmic)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < pmic->irq_mask_addr.array; i++) {
|
||||
hisi_pmic_write(pmic, pmic->irq_mask_addr.start_addr + i, HISI_MASK_STATE);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < pmic->irq_addr.array; i++) {
|
||||
unsigned int pending = hisi_pmic_read(pmic, pmic->irq_addr.start_addr + i);
|
||||
pr_debug("PMU IRQ address value:irq[0x%x] = 0x%x\n", pmic->irq_addr.start_addr + i, pending);
|
||||
hisi_pmic_write(pmic, pmic->irq_addr.start_addr + i, HISI_MASK_STATE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void hisi_pmic_irq1_prc(struct hisi_pmic *pmic)
|
||||
{
|
||||
int i;
|
||||
if(1 == g_extinterrupt_flag){
|
||||
for (i = 0 ; i < pmic->irq_mask_addr1.array; i++) {
|
||||
hisi_pmic_write(pmic, pmic->irq_mask_addr1.start_addr + i, HISI_MASK_STATE);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < pmic->irq_addr1.array; i++) {
|
||||
unsigned int pending1 = hisi_pmic_read(pmic, pmic->irq_addr1.start_addr + i);
|
||||
pr_debug("PMU IRQ address1 value:irq[0x%x] = 0x%x\n", pmic->irq_addr1.start_addr + i, pending1);
|
||||
hisi_pmic_write(pmic, pmic->irq_addr1.start_addr + i, HISI_MASK_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int hisi_pmic_probe(struct spmi_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct hisi_pmic *pmic = NULL;
|
||||
enum of_gpio_flags flags;
|
||||
int ret = 0;
|
||||
int i;
|
||||
unsigned int fpga_flag = 0;
|
||||
unsigned int virq;
|
||||
|
||||
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
|
||||
if (!pmic) {
|
||||
dev_err(dev, "cannot allocate hisi_pmic device info\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*TODO: get pmic dts info*/
|
||||
ret = get_pmic_device_tree_data(np, pmic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error reading hisi pmic dts \n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*get pmic dts the second group irq*/
|
||||
ret = get_pmic_device_tree_data1(np, pmic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "the platform don't support ext-interrupt.\n");
|
||||
}
|
||||
|
||||
/* TODO: get and enable clk request */
|
||||
spin_lock_init(&pmic->lock);
|
||||
|
||||
pmic->dev = dev;
|
||||
g_pmic = pmic;
|
||||
ret = of_property_read_u32_array(np, "hisilicon,pmic_fpga_flag", &fpga_flag, 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,pmic_fpga_flag property set\n");
|
||||
}
|
||||
if (PMIC_FPGA_FLAG == fpga_flag) {
|
||||
goto after_irq_register;
|
||||
}
|
||||
|
||||
pmic->gpio = of_get_gpio_flags(np, 0, &flags);
|
||||
if (pmic->gpio < 0)
|
||||
return pmic->gpio;
|
||||
|
||||
if (!gpio_is_valid(pmic->gpio))
|
||||
return -EINVAL;
|
||||
|
||||
ret = gpio_request_one(pmic->gpio, GPIOF_IN, "pmic");
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmic->irq = gpio_to_irq(pmic->gpio);
|
||||
|
||||
/* mask && clear IRQ status */
|
||||
hisi_pmic_irq_prc(pmic);
|
||||
/*clear && mask the new adding irq*/
|
||||
hisi_pmic_irq1_prc(pmic);
|
||||
|
||||
pmic->irqnum += pmic->irqnum1;
|
||||
|
||||
pmic->irqs = (unsigned int *)devm_kmalloc(dev, pmic->irqnum * sizeof(int), GFP_KERNEL);
|
||||
if (!pmic->irqs) {
|
||||
pr_err("%s:Failed to alloc memory for pmic irq number!\n", __func__);
|
||||
goto irq_malloc;
|
||||
}
|
||||
memset(pmic->irqs, 0, pmic->irqnum);
|
||||
|
||||
pmic->domain = irq_domain_add_simple(np, pmic->irqnum, 0,
|
||||
&hisi_domain_ops, pmic);
|
||||
if (!pmic->domain) {
|
||||
dev_err(dev, "failed irq domain add simple!\n");
|
||||
ret = -ENODEV;
|
||||
goto irq_domain;
|
||||
}
|
||||
|
||||
for (i = 0; i < pmic->irqnum; i++) {
|
||||
virq = irq_create_mapping(pmic->domain, i);
|
||||
if (virq == NO_IRQ) {
|
||||
pr_debug("Failed mapping hwirq\n");
|
||||
ret = -ENOSPC;
|
||||
goto irq_create_mapping;
|
||||
}
|
||||
pmic->irqs[i] = virq;
|
||||
pr_info("[%s]. pmic->irqs[%d] = %d\n", __func__, i, pmic->irqs[i]);
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(pmic->irq, hisi_irq_handler, NULL,
|
||||
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
|
||||
"pmic", pmic);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "could not claim pmic %d\n", ret);
|
||||
ret = -ENODEV;
|
||||
goto request_theaded_irq;
|
||||
}
|
||||
|
||||
after_irq_register:
|
||||
return 0;
|
||||
|
||||
|
||||
request_theaded_irq:
|
||||
irq_create_mapping:
|
||||
irq_domain:
|
||||
irq_malloc:
|
||||
gpio_free(pmic->gpio);
|
||||
g_pmic = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hisi_pmic_remove(struct spmi_device *pdev)
|
||||
{
|
||||
|
||||
struct hisi_pmic *pmic = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
free_irq(pmic->irq, pmic);
|
||||
gpio_free(pmic->gpio);
|
||||
devm_kfree(&pdev->dev, pmic);
|
||||
|
||||
}
|
||||
static int hisi_pmic_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct hisi_pmic *pmic = dev_get_drvdata(dev);
|
||||
|
||||
if (NULL == pmic) {
|
||||
pr_err("%s:pmic is NULL\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("%s:+\n", __func__);
|
||||
pr_info("%s:-\n", __func__);
|
||||
|
||||
return 0;
|
||||
}/*lint !e715 */
|
||||
|
||||
static int hisi_pmic_resume(struct device *dev)
|
||||
{
|
||||
struct hisi_pmic *pmic = dev_get_drvdata(dev);
|
||||
|
||||
if (NULL == pmic) {
|
||||
pr_err("%s:pmic is NULL\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("%s:+\n", __func__);
|
||||
pr_info("%s:-\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(spmi, pmic_spmi_id);
|
||||
static struct spmi_driver hisi_pmic_driver = {
|
||||
.driver = {
|
||||
.name = "hisi_pmic",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_hisi_pmic_match_tbl,
|
||||
.suspend = hisi_pmic_suspend,
|
||||
.resume = hisi_pmic_resume,
|
||||
},
|
||||
.probe = hisi_pmic_probe,
|
||||
.remove = hisi_pmic_remove,
|
||||
};
|
||||
|
||||
static int __init hisi_pmic_init(void)
|
||||
{
|
||||
return spmi_driver_register(&hisi_pmic_driver);
|
||||
}
|
||||
|
||||
static void __exit hisi_pmic_exit(void)
|
||||
{
|
||||
spmi_driver_unregister(&hisi_pmic_driver);
|
||||
}
|
||||
|
||||
|
||||
subsys_initcall_sync(hisi_pmic_init);
|
||||
module_exit(hisi_pmic_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PMIC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
Regular → Executable
+12
-1
@@ -2028,7 +2028,18 @@ static struct platform_driver pcs_driver = {
|
||||
#endif
|
||||
};
|
||||
|
||||
module_platform_driver(pcs_driver);
|
||||
static int __init pinctrl_single_init(void)
|
||||
{
|
||||
return platform_driver_register(&pcs_driver);
|
||||
}
|
||||
|
||||
static void __exit pinctrl_single_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pcs_driver);
|
||||
}
|
||||
|
||||
arch_initcall(pinctrl_single_init);
|
||||
module_exit(pinctrl_single_exit);
|
||||
|
||||
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
|
||||
MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
|
||||
|
||||
Regular → Executable
+7
@@ -288,6 +288,13 @@ config REGULATOR_HI655X
|
||||
This driver provides support for the voltage regulators of the
|
||||
Hisilicon Hi655x PMIC device.
|
||||
|
||||
config HISI_REGULATOR_SPMI
|
||||
tristate "HiSilicon PMIC regulators from spmi device"
|
||||
depends on HISI_PMIC_PMU_SPMI
|
||||
help
|
||||
This driver provides support for the voltage regulators on the
|
||||
HiSilicon Hisi PMU / Codec IC.
|
||||
|
||||
config REGULATOR_ISL9305
|
||||
tristate "Intersil ISL9305 regulator"
|
||||
depends on I2C
|
||||
|
||||
Regular → Executable
+1
@@ -37,6 +37,7 @@ obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_HI6421V530) += hi6421v530-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o
|
||||
obj-$(CONFIG_HISI_REGULATOR_SPMI) += hisi_regulator_spmi.o
|
||||
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
|
||||
obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o
|
||||
|
||||
Executable
+741
@@ -0,0 +1,741 @@
|
||||
/*
|
||||
* Device driver for regulators in Hisi IC
|
||||
*
|
||||
* Copyright (c) 2013 Linaro Ltd.
|
||||
* Copyright (c) 2011 Hisilicon.
|
||||
*
|
||||
* Guodong Xu <guodong.xu@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/mfd/hisi_pmic.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/version.h>
|
||||
#ifdef CONFIG_HISI_PMIC_DEBUG
|
||||
#include <linux/debugfs.h>
|
||||
#endif
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
#if 1
|
||||
#define BRAND_DEBUG(args...) pr_debug(args);
|
||||
#else
|
||||
#define BRAND_DEBUG(args...)
|
||||
#endif
|
||||
|
||||
struct hisi_regulator_register_info {
|
||||
u32 ctrl_reg;
|
||||
u32 enable_mask;
|
||||
u32 eco_mode_mask;
|
||||
u32 vset_reg;
|
||||
u32 vset_mask;
|
||||
};
|
||||
|
||||
struct hisi_regulator {
|
||||
const char *name;
|
||||
struct hisi_regulator_register_info register_info;
|
||||
struct timeval last_off_time;
|
||||
u32 off_on_delay;
|
||||
u32 eco_uA;
|
||||
struct regulator_desc rdesc;
|
||||
int (*dt_parse)(struct hisi_regulator *, struct spmi_device *);
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(enable_mutex);
|
||||
struct timeval last_enabled;
|
||||
|
||||
|
||||
static inline struct hisi_pmic *rdev_to_pmic(struct regulator_dev *dev)
|
||||
{
|
||||
/* regulator_dev parent to->
|
||||
* hisi regulator platform device_dev parent to->
|
||||
* hisi pmic platform device_dev
|
||||
*/
|
||||
return dev_get_drvdata(rdev_get_dev(dev)->parent->parent);
|
||||
}
|
||||
|
||||
/* helper function to ensure when it returns it is at least 'delay_us'
|
||||
* microseconds after 'since'.
|
||||
*/
|
||||
static void ensured_time_after(struct timeval since, u32 delay_us)
|
||||
{
|
||||
struct timeval now;
|
||||
u64 elapsed_ns64, delay_ns64;
|
||||
u32 actual_us32;
|
||||
|
||||
delay_ns64 = delay_us * NSEC_PER_USEC;
|
||||
do_gettimeofday(&now);
|
||||
elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since);
|
||||
if (delay_ns64 > elapsed_ns64) {
|
||||
actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) /
|
||||
NSEC_PER_USEC);
|
||||
if (actual_us32 >= 1000) {
|
||||
mdelay(actual_us32 / 1000); /*lint !e647 */
|
||||
udelay(actual_us32 % 1000);
|
||||
} else if (actual_us32 > 0) {
|
||||
udelay(actual_us32);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int hisi_regulator_is_enabled(struct regulator_dev *dev)
|
||||
{
|
||||
u32 reg_val;
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
|
||||
reg_val = hisi_pmic_read(pmic, sreg->register_info.ctrl_reg);
|
||||
BRAND_DEBUG("<[%s]: ctrl_reg=0x%x,enable_state=%d>\n", __func__, sreg->register_info.ctrl_reg,\
|
||||
(reg_val & sreg->register_info.enable_mask));
|
||||
|
||||
return ((reg_val & sreg->register_info.enable_mask) != 0);
|
||||
}
|
||||
|
||||
static int hisi_regulator_enable(struct regulator_dev *dev)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
|
||||
/* keep a distance of off_on_delay from last time disabled */
|
||||
ensured_time_after(sreg->last_off_time, sreg->off_on_delay);
|
||||
|
||||
BRAND_DEBUG("<[%s]: off_on_delay=%dus>\n", __func__, sreg->off_on_delay);
|
||||
|
||||
/* cannot enable more than one regulator at one time */
|
||||
mutex_lock(&enable_mutex);
|
||||
ensured_time_after(last_enabled, HISI_REGS_ENA_PROTECT_TIME);
|
||||
|
||||
/* set enable register */
|
||||
hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
|
||||
sreg->register_info.enable_mask,
|
||||
sreg->register_info.enable_mask);
|
||||
BRAND_DEBUG("<[%s]: ctrl_reg=0x%x,enable_mask=0x%x>\n", __func__, sreg->register_info.ctrl_reg,\
|
||||
sreg->register_info.enable_mask);
|
||||
|
||||
do_gettimeofday(&last_enabled);
|
||||
mutex_unlock(&enable_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_regulator_disable(struct regulator_dev *dev)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
|
||||
/* set enable register to 0 */
|
||||
hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
|
||||
sreg->register_info.enable_mask, 0);
|
||||
|
||||
do_gettimeofday(&sreg->last_off_time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_regulator_get_voltage(struct regulator_dev *dev)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
u32 reg_val, selector;
|
||||
|
||||
/* get voltage selector */
|
||||
reg_val = hisi_pmic_read(pmic, sreg->register_info.vset_reg);
|
||||
BRAND_DEBUG("<[%s]: vset_reg=0x%x>\n", __func__, sreg->register_info.vset_reg);
|
||||
|
||||
selector = (reg_val & sreg->register_info.vset_mask) >>
|
||||
(ffs(sreg->register_info.vset_mask) - 1);
|
||||
|
||||
return sreg->rdesc.ops->list_voltage(dev, selector);
|
||||
}
|
||||
|
||||
static int hisi_regulator_set_voltage(struct regulator_dev *dev,
|
||||
int min_uV, int max_uV, unsigned *selector)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
u32 vsel;
|
||||
int ret = 0;
|
||||
|
||||
for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) {
|
||||
int uV = sreg->rdesc.volt_table[vsel];
|
||||
/* Break at the first in-range value */
|
||||
if (min_uV <= uV && uV <= max_uV)
|
||||
break;
|
||||
}
|
||||
|
||||
/* unlikely to happen. sanity test done by regulator core */
|
||||
if (unlikely(vsel == sreg->rdesc.n_voltages))
|
||||
return -EINVAL;
|
||||
|
||||
*selector = vsel;
|
||||
/* set voltage selector */
|
||||
hisi_pmic_rmw(pmic, sreg->register_info.vset_reg,
|
||||
sreg->register_info.vset_mask,
|
||||
vsel << (ffs(sreg->register_info.vset_mask) - 1));
|
||||
|
||||
BRAND_DEBUG("<[%s]: vset_reg=0x%x, vset_mask=0x%x, value=0x%x>\n", __func__,\
|
||||
sreg->register_info.vset_reg,\
|
||||
sreg->register_info.vset_mask,\
|
||||
vsel << (ffs(sreg->register_info.vset_mask) - 1)\
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int hisi_regulator_get_mode(struct regulator_dev *dev)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = hisi_pmic_read(pmic, sreg->register_info.ctrl_reg);
|
||||
BRAND_DEBUG("<[%s]: reg_val=%d, ctrl_reg=0x%x, eco_mode_mask=0x%x>\n", __func__, reg_val,\
|
||||
sreg->register_info.ctrl_reg,\
|
||||
sreg->register_info.eco_mode_mask\
|
||||
);
|
||||
|
||||
if (reg_val & sreg->register_info.eco_mode_mask)
|
||||
return REGULATOR_MODE_IDLE;
|
||||
else
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static int hisi_regulator_set_mode(struct regulator_dev *dev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
struct hisi_pmic *pmic = rdev_to_pmic(dev);
|
||||
u32 eco_mode;
|
||||
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_NORMAL:
|
||||
eco_mode = HISI_ECO_MODE_DISABLE;
|
||||
break;
|
||||
case REGULATOR_MODE_IDLE:
|
||||
eco_mode = HISI_ECO_MODE_ENABLE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set mode */
|
||||
hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
|
||||
sreg->register_info.eco_mode_mask,
|
||||
eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1));
|
||||
|
||||
BRAND_DEBUG("<[%s]: ctrl_reg=0x%x, eco_mode_mask=0x%x, value=0x%x>\n", __func__,\
|
||||
sreg->register_info.ctrl_reg,\
|
||||
sreg->register_info.eco_mode_mask,\
|
||||
eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1)\
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int hisi_regulator_get_optimum_mode(struct regulator_dev *dev,
|
||||
int input_uV, int output_uV, int load_uA)
|
||||
{
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(dev);
|
||||
|
||||
if ((load_uA == 0) || ((unsigned int)load_uA > sreg->eco_uA))
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
else
|
||||
return REGULATOR_MODE_IDLE;
|
||||
}
|
||||
|
||||
static int hisi_dt_parse_common(struct hisi_regulator *sreg,
|
||||
struct spmi_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct regulator_desc *rdesc = &sreg->rdesc;
|
||||
unsigned int register_info[3] = {0};
|
||||
int ret = 0;
|
||||
|
||||
/* parse .register_info.ctrl_reg */
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-ctrl",
|
||||
register_info, 3);
|
||||
if (ret) {
|
||||
dev_err(dev, "no hisilicon,hisi-ctrl property set\n");
|
||||
goto dt_parse_common_end;
|
||||
}
|
||||
sreg->register_info.ctrl_reg = register_info[0];
|
||||
sreg->register_info.enable_mask = register_info[1];
|
||||
sreg->register_info.eco_mode_mask = register_info[2];
|
||||
|
||||
/* parse .register_info.vset_reg */
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-vset",
|
||||
register_info, 2);
|
||||
if (ret) {
|
||||
dev_err(dev, "no hisilicon,hisi-vset property set\n");
|
||||
goto dt_parse_common_end;
|
||||
}
|
||||
sreg->register_info.vset_reg = register_info[0];
|
||||
sreg->register_info.vset_mask = register_info[1];
|
||||
|
||||
/* parse .off-on-delay */
|
||||
ret = of_property_read_u32(np, "hisilicon,hisi-off-on-delay-us",
|
||||
&sreg->off_on_delay);
|
||||
if (ret) {
|
||||
dev_err(dev, "no hisilicon,hisi-off-on-delay-us property set\n");
|
||||
goto dt_parse_common_end;
|
||||
}
|
||||
|
||||
/* parse .enable_time */
|
||||
ret = of_property_read_u32(np, "hisilicon,hisi-enable-time-us",
|
||||
&rdesc->enable_time);
|
||||
if (ret) {
|
||||
dev_err(dev, "no hisilicon,hisi-enable-time-us property set\n");
|
||||
goto dt_parse_common_end;
|
||||
}
|
||||
|
||||
/* parse .eco_uA */
|
||||
ret = of_property_read_u32(np, "hisilicon,hisi-eco-microamp",
|
||||
&sreg->eco_uA);
|
||||
if (ret) {
|
||||
sreg->eco_uA = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
dt_parse_common_end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hisi_dt_parse_ldo(struct hisi_regulator *sreg,
|
||||
struct spmi_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct regulator_desc *rdesc = &sreg->rdesc;
|
||||
unsigned int *v_table;
|
||||
int ret = 0;
|
||||
|
||||
/* parse .n_voltages, and .volt_table */
|
||||
ret = of_property_read_u32(np, "hisilicon,hisi-n-voltages",
|
||||
&rdesc->n_voltages);
|
||||
if (ret) {
|
||||
dev_err(dev, "no hisilicon,hisi-n-voltages property set\n");
|
||||
goto dt_parse_ldo_end;
|
||||
}
|
||||
|
||||
/* alloc space for .volt_table */
|
||||
v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
|
||||
GFP_KERNEL);
|
||||
if (unlikely(!v_table)) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(dev, "no memory for .volt_table\n");
|
||||
goto dt_parse_ldo_end;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np, "hisilicon,hisi-vset-table",
|
||||
v_table, rdesc->n_voltages);
|
||||
if (ret) {
|
||||
dev_err(dev, "no hisilicon,hisi-vset-table property set\n");
|
||||
goto dt_parse_ldo_end1;
|
||||
}
|
||||
rdesc->volt_table = v_table;
|
||||
|
||||
/* parse hisi regulator's dt common part */
|
||||
ret = hisi_dt_parse_common(sreg, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failure in hisi_dt_parse_common\n");
|
||||
goto dt_parse_ldo_end1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
dt_parse_ldo_end1:
|
||||
dt_parse_ldo_end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct regulator_ops hisi_ldo_rops = {
|
||||
.is_enabled = hisi_regulator_is_enabled,
|
||||
.enable = hisi_regulator_enable,
|
||||
.disable = hisi_regulator_disable,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.get_voltage = hisi_regulator_get_voltage,
|
||||
.set_voltage = hisi_regulator_set_voltage,
|
||||
.get_mode = hisi_regulator_get_mode,
|
||||
.set_mode = hisi_regulator_set_mode,
|
||||
.get_optimum_mode = hisi_regulator_get_optimum_mode,
|
||||
};
|
||||
|
||||
static const struct hisi_regulator hisi_regulator_ldo = {
|
||||
.rdesc = {
|
||||
.ops = &hisi_ldo_rops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.dt_parse = hisi_dt_parse_ldo,
|
||||
};
|
||||
|
||||
static struct of_device_id of_hisi_regulator_match_tbl[] = {
|
||||
{
|
||||
.compatible = "hisilicon-hisi-ldo",
|
||||
.data = &hisi_regulator_ldo,
|
||||
},
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HISI_PMIC_DEBUG
|
||||
extern void get_current_regulator_dev(struct seq_file *s);
|
||||
extern void set_regulator_state(char *ldo_name, int value);
|
||||
extern void get_regulator_state(char *ldo_name);
|
||||
extern int set_regulator_voltage(char *ldo_name, unsigned int vol_value);
|
||||
|
||||
u32 pmu_atoi(char *s)
|
||||
{
|
||||
char *p = s;
|
||||
char c;
|
||||
u64 ret = 0;
|
||||
if (s == NULL)
|
||||
return 0;
|
||||
while ((c = *p++) != '\0') {
|
||||
if ('0' <= c && c <= '9') {
|
||||
ret *= 10;
|
||||
ret += (u64)((unsigned char)c - '0');
|
||||
if (ret > U32_MAX)
|
||||
return 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (u32)ret;
|
||||
}
|
||||
static int dbg_hisi_regulator_show(struct seq_file *s, void *data)
|
||||
{
|
||||
seq_printf(s, "\n\r");
|
||||
seq_printf(s, "%-13s %-15s %-15s %-15s %-15s\n\r",
|
||||
"LDO_NAME", "ON/OFF", "Use_count", "Open_count", "Always_on");
|
||||
seq_printf(s, "-----------------------------------------"
|
||||
"-----------------------------------------------\n\r");
|
||||
get_current_regulator_dev(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbg_hisi_regulator_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dbg_hisi_regulator_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations debug_regulator_state_fops = {
|
||||
.open = dbg_hisi_regulator_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dbg_control_regulator_show(struct seq_file *s, void *data)
|
||||
{
|
||||
printk(" \n\r \
|
||||
---------------------------------------------------------------------------------\n\r \
|
||||
|usage: |\n\r \
|
||||
| S = state R = read V = voltage |\n\r \
|
||||
| set ldo state and voltage |\n\r \
|
||||
| get ldo state and current voltage |\n\r \
|
||||
|example: |\n\r \
|
||||
| echo S ldo16 0 > control_regulator :disable ldo16 |\n\r \
|
||||
| echo S ldo16 1 > control_regulator :enable ldo16 |\n\r \
|
||||
| echo R ldo16 > control_regulator :get ldo16 state and voltage |\n\r \
|
||||
| echo V ldo16 xxx > control_regulator :set ldo16 voltage |\n\r \
|
||||
---------------------------------------------------------------------------------\n\r");
|
||||
return 0;
|
||||
}
|
||||
static ssize_t dbg_control_regulator_set_value(struct file *filp, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char tmp[128] = {0};
|
||||
char ptr[128] = {0};
|
||||
char *vol = NULL;
|
||||
char num = 0;
|
||||
unsigned int i;
|
||||
int next_flag = 1;
|
||||
|
||||
if (count >= 128) {
|
||||
pr_info("error! buffer size big than internal buffer\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmp, buffer, count)) {
|
||||
pr_info("error!\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (tmp[0] == 'R' || tmp[0] == 'r') {
|
||||
for (i = 2; i < (count - 1); i++) {
|
||||
ptr[i - 2] = tmp[i];
|
||||
}
|
||||
ptr[i - 2] = '\0';
|
||||
get_regulator_state(ptr);
|
||||
} else if (tmp[0] == 'S' || tmp[0] == 's') {
|
||||
for (i = 2; i < (count - 1); i++) {
|
||||
if (tmp[i] == ' ') {
|
||||
next_flag = 0;
|
||||
ptr[i - 2] = '\0';
|
||||
continue;
|
||||
}
|
||||
if (next_flag) {
|
||||
ptr[i - 2] = tmp[i];
|
||||
} else {
|
||||
num = tmp[i] - 48;
|
||||
}
|
||||
}
|
||||
set_regulator_state(ptr, num);
|
||||
} else if (tmp[0] == 'V' || tmp[0] == 'v') {
|
||||
for (i = 2; i < (count - 1); i++) {
|
||||
if (tmp[i] == ' ') {
|
||||
next_flag = 0;
|
||||
ptr[i - 2] = '\0';
|
||||
continue;
|
||||
}
|
||||
if (next_flag) {
|
||||
ptr[i - 2] = tmp[i];
|
||||
} else {
|
||||
vol = &tmp[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_regulator_voltage(ptr, pmu_atoi(vol));
|
||||
}
|
||||
|
||||
*ppos += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int dbg_control_regulator_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return single_open(file, dbg_control_regulator_show, &inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations set_control_regulator_fops = {
|
||||
.open = dbg_control_regulator_open,
|
||||
.read = seq_read,
|
||||
.write = dbg_control_regulator_set_value,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int hisi_regulator_probe(struct spmi_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct regulator_desc *rdesc;
|
||||
struct regulator_dev *rdev;
|
||||
struct hisi_regulator *sreg = NULL;
|
||||
struct regulator_init_data *initdata;
|
||||
struct regulator_config config = { };
|
||||
const struct of_device_id *match;
|
||||
struct regulation_constraints *constraint;
|
||||
const char *supplyname = NULL;
|
||||
#ifdef CONFIG_HISI_PMIC_DEBUG
|
||||
struct dentry *d;
|
||||
static int debugfs_flag;
|
||||
#endif
|
||||
unsigned int temp_modes;
|
||||
|
||||
const struct hisi_regulator *template = NULL;
|
||||
int ret = 0;
|
||||
/* to check which type of regulator this is */
|
||||
match = of_match_device(of_hisi_regulator_match_tbl, &pdev->dev);
|
||||
if (NULL == match) {
|
||||
pr_err("get hisi regulator fail!\n\r");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
template = match->data;
|
||||
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0))
|
||||
initdata = of_get_regulator_init_data(dev, np, NULL);
|
||||
#else
|
||||
initdata = of_get_regulator_init_data(dev, np);
|
||||
#endif
|
||||
if (NULL == initdata) {
|
||||
pr_err("get regulator init data error !\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* hisi regulator supports two modes */
|
||||
constraint = &initdata->constraints;
|
||||
|
||||
ret = of_property_read_u32_array(np, "hisilicon,valid-modes-mask",
|
||||
&(constraint->valid_modes_mask), 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,valid-modes-mask property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
ret = of_property_read_u32_array(np, "hisilicon,valid-idle-mask",
|
||||
&temp_modes, 1);
|
||||
if (ret) {
|
||||
pr_err("no hisilicon,valid-modes-mask property set\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
constraint->valid_ops_mask |= temp_modes;
|
||||
|
||||
sreg = kmemdup(template, sizeof(*sreg), GFP_KERNEL);
|
||||
if (!sreg) {
|
||||
pr_err("template kememdup is fail. \n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
sreg->name = initdata->constraints.name;
|
||||
rdesc = &sreg->rdesc;
|
||||
rdesc->name = sreg->name;
|
||||
rdesc->min_uV = initdata->constraints.min_uV;
|
||||
supplyname = of_get_property(np, "hisilicon,supply_name", NULL);
|
||||
if (supplyname != NULL) {
|
||||
initdata->supply_regulator = supplyname;
|
||||
}
|
||||
|
||||
/* to parse device tree data for regulator specific */
|
||||
ret = sreg->dt_parse(sreg, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "device tree parameter parse error!\n");
|
||||
goto hisi_probe_end;
|
||||
}
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.init_data = initdata;
|
||||
config.driver_data = sreg;
|
||||
config.of_node = pdev->dev.of_node;
|
||||
|
||||
/* register regulator */
|
||||
rdev = regulator_register(rdesc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "failed to register %s\n",
|
||||
rdesc->name);
|
||||
ret = PTR_ERR(rdev);
|
||||
goto hisi_probe_end;
|
||||
}
|
||||
|
||||
BRAND_DEBUG("[%s]:valid_modes_mask[0x%x], valid_ops_mask[0x%x]\n", rdesc->name,\
|
||||
constraint->valid_modes_mask, constraint->valid_ops_mask);
|
||||
|
||||
dev_set_drvdata(dev, rdev);
|
||||
#ifdef CONFIG_HISI_PMIC_DEBUG
|
||||
if (debugfs_flag == 0) {
|
||||
d = debugfs_create_dir("hisi_regulator_debugfs", NULL);
|
||||
if (!d) {
|
||||
dev_err(dev, "failed to create hisi regulator debugfs dir !\n");
|
||||
ret = -ENOMEM;
|
||||
goto hisi_probe_fail;
|
||||
}
|
||||
(void) debugfs_create_file("regulator_state", S_IRUSR,
|
||||
d, NULL, &debug_regulator_state_fops);
|
||||
|
||||
(void) debugfs_create_file("control_regulator", S_IRUSR,
|
||||
d, NULL, &set_control_regulator_fops);
|
||||
debugfs_flag = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HISI_PMIC_DEBUG
|
||||
hisi_probe_fail:
|
||||
if (ret)
|
||||
regulator_unregister(rdev);
|
||||
#endif
|
||||
hisi_probe_end:
|
||||
if (ret)
|
||||
kfree(sreg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hisi_regulator_remove(struct spmi_device *pdev)
|
||||
{
|
||||
struct regulator_dev *rdev = dev_get_drvdata(&pdev->dev);
|
||||
struct hisi_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
|
||||
regulator_unregister(rdev);
|
||||
|
||||
/* TODO: should i worry about that? devm_kzalloc */
|
||||
if (sreg->rdesc.volt_table)
|
||||
devm_kfree(&pdev->dev, (unsigned int *)sreg->rdesc.volt_table);
|
||||
|
||||
kfree(sreg);
|
||||
}
|
||||
static int hisi_regulator_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct hisi_regulator *hisi_regulator = dev_get_drvdata(dev);
|
||||
|
||||
if (NULL == hisi_regulator) {
|
||||
pr_err("%s:regulator is NULL\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("%s:+\n", __func__);
|
||||
pr_info("%s:-\n", __func__);
|
||||
|
||||
return 0;
|
||||
}/*lint !e715 */
|
||||
|
||||
static int hisi_regulator_resume(struct device *dev)
|
||||
{
|
||||
struct hisi_regulator *hisi_regulator = dev_get_drvdata(dev);
|
||||
|
||||
if (NULL == hisi_regulator) {
|
||||
pr_err("%s:regulator is NULL\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("%s:+\n", __func__);
|
||||
pr_info("%s:-\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spmi_driver hisi_pmic_driver = {
|
||||
.driver = {
|
||||
.name = "hisi_regulator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_hisi_regulator_match_tbl,
|
||||
.suspend = hisi_regulator_suspend,
|
||||
.resume = hisi_regulator_resume,
|
||||
},
|
||||
.probe = hisi_regulator_probe,
|
||||
.remove = hisi_regulator_remove,
|
||||
};
|
||||
|
||||
static int __init hisi_regulator_init(void)
|
||||
{
|
||||
return spmi_driver_register(&hisi_pmic_driver);
|
||||
}
|
||||
|
||||
static void __exit hisi_regulator_exit(void)
|
||||
{
|
||||
spmi_driver_unregister(&hisi_pmic_driver);
|
||||
}
|
||||
|
||||
fs_initcall(hisi_regulator_init);
|
||||
module_exit(hisi_regulator_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Hisi regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
Executable
+390
@@ -0,0 +1,390 @@
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/spmi.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
#define SPMI_CONTROLLER_NAME "spmi_controller"
|
||||
|
||||
/*
|
||||
* SPMI register addr
|
||||
*/
|
||||
#define SPMI_CHANNEL_OFFSET 0x0300
|
||||
#define SPMI_SLAVE_OFFSET 0x20
|
||||
|
||||
#define SPMI_APB_SPMI_CMD_BASE_ADDR 0x0100
|
||||
/*lint -e750 -esym(750,*)*/
|
||||
#define SPMI_APB_SPMI_WDATA0_BASE_ADDR 0x0104
|
||||
#define SPMI_APB_SPMI_WDATA1_BASE_ADDR 0x0108
|
||||
#define SPMI_APB_SPMI_WDATA2_BASE_ADDR 0x010c
|
||||
#define SPMI_APB_SPMI_WDATA3_BASE_ADDR 0x0110
|
||||
|
||||
#define SPMI_APB_SPMI_STATUS_BASE_ADDR 0x0200
|
||||
|
||||
#define SPMI_APB_SPMI_RDATA0_BASE_ADDR 0x0204
|
||||
#define SPMI_APB_SPMI_RDATA1_BASE_ADDR 0x0208
|
||||
#define SPMI_APB_SPMI_RDATA2_BASE_ADDR 0x020c
|
||||
#define SPMI_APB_SPMI_RDATA3_BASE_ADDR 0x0210
|
||||
/*lint +e750 -esym(750,*)*/
|
||||
|
||||
#define SPMI_PER_DATAREG_BYTE 4
|
||||
/*
|
||||
* SPMI cmd register
|
||||
*/
|
||||
#define SPMI_APB_SPMI_CMD_EN (1 << 31)
|
||||
#define SPMI_APB_SPMI_CMD_TYPE_OFFSET 24
|
||||
#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET 20
|
||||
#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET 16
|
||||
#define SPMI_APB_SPMI_CMD_ADDR_OFFSET 0
|
||||
|
||||
#define Tranverse32(X) ((((u32)(X) & 0xff000000) >> 24) | \
|
||||
(((u32)(X) & 0x00ff0000) >> 8) | \
|
||||
(((u32)(X) & 0x0000ff00) << 8) | \
|
||||
(((u32)(X) & 0x000000ff) << 24))
|
||||
|
||||
/* Command Opcodes */
|
||||
/*lint -e749 -esym(749,*)*/
|
||||
enum spmi_controller_cmd_op_code {
|
||||
SPMI_CMD_REG_ZERO_WRITE = 0,
|
||||
SPMI_CMD_REG_WRITE = 1,
|
||||
SPMI_CMD_REG_READ = 2,
|
||||
SPMI_CMD_EXT_REG_WRITE = 3,
|
||||
SPMI_CMD_EXT_REG_READ = 4,
|
||||
SPMI_CMD_EXT_REG_WRITE_L = 5,
|
||||
SPMI_CMD_EXT_REG_READ_L = 6,
|
||||
SPMI_CMD_REG_RESET = 7,
|
||||
SPMI_CMD_REG_SLEEP = 8,
|
||||
SPMI_CMD_REG_SHUTDOWN = 9,
|
||||
SPMI_CMD_REG_WAKEUP = 10,
|
||||
};
|
||||
/*lint +e749 -esym(749,*)*/
|
||||
|
||||
/*
|
||||
* SPMI status register
|
||||
*/
|
||||
#define SPMI_APB_TRANS_DONE (1 << 0)
|
||||
#define SPMI_APB_TRANS_FAIL (1 << 2)
|
||||
|
||||
/* Command register fields */
|
||||
#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16
|
||||
|
||||
/* Maximum number of support PMIC peripherals */
|
||||
#define SPMI_CONTROLLER_TIMEOUT_US 1000
|
||||
#define SPMI_CONTROLLER_MAX_TRANS_BYTES (16)
|
||||
|
||||
#define SPMI_WRITEL( dev, reg, addr ) \
|
||||
do { \
|
||||
writel( ( reg ), ( addr ) ); \
|
||||
} while (0)
|
||||
|
||||
#define SPMI_READL( dev, reg, addr ) \
|
||||
do { \
|
||||
reg = readl( addr ); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* @base base address of the PMIC Arbiter core registers.
|
||||
* @rdbase, @wrbase base address of the PMIC Arbiter read core registers.
|
||||
* For HW-v1 these are equal to base.
|
||||
* For HW-v2, the value is the same in eeraly probing, in order to read
|
||||
* PMIC_ARB_CORE registers, then chnls, and obsrvr are set to
|
||||
* PMIC_ARB_CORE_REGISTERS and PMIC_ARB_CORE_REGISTERS_OBS respectivly.
|
||||
* @intr base address of the SPMI interrupt control registers
|
||||
* @ppid_2_chnl_tbl lookup table f(SID, Periph-ID) -> channel num
|
||||
* entry is only valid if corresponding bit is set in valid_ppid_bitmap.
|
||||
* @valid_ppid_bitmap bit is set only for valid ppids.
|
||||
* @fmt_cmd formats a command to be set into PMIC_ARBq_CHNLn_CMD
|
||||
* @chnl_ofst calculates offset of the base of a channel reg space
|
||||
* @ee execution environment id
|
||||
* @irq_acc0_init_val initial value of the interrupt accumulator at probe time.
|
||||
* Use for an HW workaround. On handling interrupts, the first accumulator
|
||||
* register will be compared against this value, and bits which are set at
|
||||
* boot will be ignored.
|
||||
* @reserved_chnl entry of ppid_2_chnl_tbl that this driver should never touch.
|
||||
* value is positive channel number or negative to mark it unused.
|
||||
*/
|
||||
struct spmi_controller_dev {
|
||||
struct spmi_controller *controller;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
u32 channel;
|
||||
};
|
||||
|
||||
static int spmi_controller_wait_for_done(struct spmi_controller_dev *ctrl_dev,
|
||||
void __iomem *base, u8 sid, u16 addr)
|
||||
{
|
||||
u32 status = 0;
|
||||
u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
|
||||
u32 offset = SPMI_APB_SPMI_STATUS_BASE_ADDR + SPMI_CHANNEL_OFFSET * ctrl_dev->channel
|
||||
+ SPMI_SLAVE_OFFSET * sid;
|
||||
|
||||
while (timeout--) {
|
||||
SPMI_READL(ctrl_dev->dev, status, base + offset);/*lint !e732 */
|
||||
|
||||
if (status & SPMI_APB_TRANS_DONE) {
|
||||
if (status & SPMI_APB_TRANS_FAIL) {
|
||||
dev_err(ctrl_dev->dev,
|
||||
"%s: transaction failed (0x%x)\n",
|
||||
__func__, status);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
udelay(1);/*lint !e778 !e774 !e747*/
|
||||
}
|
||||
|
||||
dev_err(ctrl_dev->dev,
|
||||
"%s: timeout, status 0x%x\n",
|
||||
__func__, status);
|
||||
return -ETIMEDOUT;/*lint !e438*/
|
||||
}/*lint !e715 !e529*/
|
||||
|
||||
static int spmi_read_cmd(struct spmi_controller *ctrl,
|
||||
u8 opc, u8 sid, u16 addr, u8 *buf, size_t bc)
|
||||
{
|
||||
struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
|
||||
unsigned long flags;
|
||||
u32 cmd, data;
|
||||
int rc;
|
||||
u32 chnl_ofst = SPMI_CHANNEL_OFFSET*spmi_controller->channel;
|
||||
u8 op_code, i;
|
||||
|
||||
if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
|
||||
dev_err(spmi_controller->dev
|
||||
, "spmi_controller supports 1..%d bytes per trans, but:%ld requested"
|
||||
, SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check the opcode */
|
||||
if (SPMI_CMD_READ == opc)
|
||||
op_code = SPMI_CMD_REG_READ;
|
||||
else if (SPMI_CMD_EXT_READ == opc)
|
||||
op_code = SPMI_CMD_EXT_REG_READ;
|
||||
else if (SPMI_CMD_EXT_READL == opc)
|
||||
op_code = SPMI_CMD_EXT_REG_READ_L;
|
||||
else {
|
||||
dev_err(spmi_controller->dev, "invalid read cmd 0x%x", opc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd = SPMI_APB_SPMI_CMD_EN |/*lint !e648 !e701 */ /* cmd_en */
|
||||
(op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |/*lint !e648 !e701 */ /* cmd_type */
|
||||
((bc-1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |/*lint !e648 !e701 */ /* byte_cnt */
|
||||
((sid & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | /* slvid */
|
||||
((addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */
|
||||
|
||||
spin_lock_irqsave(&spmi_controller->lock, flags);/*lint !e550 */
|
||||
|
||||
SPMI_WRITEL(spmi_controller->dev, cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
|
||||
|
||||
|
||||
rc = spmi_controller_wait_for_done(spmi_controller, spmi_controller->base, sid, addr);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
SPMI_READL(spmi_controller->dev, data, spmi_controller->base + chnl_ofst + SPMI_SLAVE_OFFSET*sid + SPMI_APB_SPMI_RDATA0_BASE_ADDR + i*SPMI_PER_DATAREG_BYTE);/*lint !e732 */
|
||||
data = Tranverse32(data);
|
||||
if ((bc - i*SPMI_PER_DATAREG_BYTE ) >> 2) {/*lint !e702 */
|
||||
memcpy(buf, &data, sizeof(data));
|
||||
buf += sizeof(data);
|
||||
} else {
|
||||
memcpy(buf, &data, bc%SPMI_PER_DATAREG_BYTE);/*lint !e747 */
|
||||
buf += (bc%SPMI_PER_DATAREG_BYTE);
|
||||
}
|
||||
i++;
|
||||
} while (bc > i*SPMI_PER_DATAREG_BYTE);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&spmi_controller->lock, flags);
|
||||
if (rc)
|
||||
dev_err(spmi_controller->dev, "spmi read wait timeout op:0x%x sid:%d addr:0x%x bc:%ld\n",
|
||||
opc, sid, addr, bc + 1);
|
||||
return rc;
|
||||
}/*lint !e550 !e529*/
|
||||
|
||||
/*lint -e438 -esym(438,*)*/
|
||||
static int spmi_write_cmd(struct spmi_controller *ctrl,
|
||||
u8 opc, u8 sid, u16 addr, const u8 *buf, size_t bc)
|
||||
{
|
||||
struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
|
||||
unsigned long flags;
|
||||
u32 cmd;
|
||||
u32 data = 0;
|
||||
int rc;
|
||||
u32 chnl_ofst = SPMI_CHANNEL_OFFSET*spmi_controller->channel;
|
||||
u8 op_code, i;
|
||||
|
||||
|
||||
if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
|
||||
dev_err(spmi_controller->dev
|
||||
, "spmi_controller supports 1..%d bytes per trans, but:%ld requested"
|
||||
, SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check the opcode */
|
||||
if (SPMI_CMD_WRITE == opc)
|
||||
op_code = SPMI_CMD_REG_WRITE;
|
||||
else if (SPMI_CMD_EXT_WRITE == opc)
|
||||
op_code = SPMI_CMD_EXT_REG_WRITE;
|
||||
else if (SPMI_CMD_EXT_WRITEL == opc)
|
||||
op_code = SPMI_CMD_EXT_REG_WRITE_L;
|
||||
else {
|
||||
dev_err(spmi_controller->dev, "invalid write cmd 0x%x", opc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd = SPMI_APB_SPMI_CMD_EN |/*lint !e648 !e701 */ /* cmd_en */
|
||||
(op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |/*lint !e648 !e701 */ /* cmd_type */
|
||||
((bc-1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |/*lint !e648 !e701 */ /* byte_cnt */
|
||||
((sid & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) | /* slvid */
|
||||
((addr & 0xffff) << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */
|
||||
|
||||
/* Write data to FIFOs */
|
||||
spin_lock_irqsave(&spmi_controller->lock, flags);/*lint !e550 */
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
memset(&data, 0, sizeof(data));
|
||||
if ((bc - i*SPMI_PER_DATAREG_BYTE ) >> 2) {/*lint !e702 */
|
||||
memcpy(&data, buf, sizeof(data));
|
||||
buf +=sizeof(data);
|
||||
} else {
|
||||
memcpy(&data, buf, bc%SPMI_PER_DATAREG_BYTE);/*lint !e747 */
|
||||
buf +=(bc%SPMI_PER_DATAREG_BYTE);
|
||||
}
|
||||
|
||||
data = Tranverse32(data);
|
||||
SPMI_WRITEL(spmi_controller->dev, data, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_WDATA0_BASE_ADDR+SPMI_PER_DATAREG_BYTE*i);
|
||||
i++;
|
||||
} while (bc > i*SPMI_PER_DATAREG_BYTE);
|
||||
|
||||
/* Start the transaction */
|
||||
SPMI_WRITEL(spmi_controller->dev, cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
|
||||
|
||||
rc = spmi_controller_wait_for_done(spmi_controller, spmi_controller->base, sid, addr);
|
||||
spin_unlock_irqrestore(&spmi_controller->lock, flags);
|
||||
|
||||
if (rc)
|
||||
dev_err(spmi_controller->dev, "spmi write wait timeout op:0x%x sid:%d addr:0x%x bc:%ld\n",
|
||||
opc, sid, addr, bc);
|
||||
|
||||
return rc;
|
||||
}/*lint !e438 !e550 !e529*/
|
||||
/*lint +e438 -esym(438,*)*/
|
||||
static int spmi_controller_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spmi_controller_dev *spmi_controller;
|
||||
struct spmi_controller *ctrl;
|
||||
struct resource *iores;
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_INFO "HISI SPMI probe\n");
|
||||
ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
|
||||
if (!ctrl) {
|
||||
dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
|
||||
return -ENOMEM; /*lint !e429*/
|
||||
}
|
||||
spmi_controller = spmi_controller_get_drvdata(ctrl);
|
||||
spmi_controller->controller = ctrl;
|
||||
|
||||
/* NOTE: driver uses the static register mapping */
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iores) {
|
||||
dev_err(&pdev->dev, "can not get resource! \n");
|
||||
return -EINVAL; /*lint !e429*/
|
||||
}
|
||||
|
||||
spmi_controller->base = ioremap(iores->start, resource_size(iores));
|
||||
if (!spmi_controller->base) {
|
||||
dev_err(&pdev->dev, "can not remap base addr! \n");
|
||||
return -EADDRNOTAVAIL; /*lint !e429*/
|
||||
}
|
||||
dev_dbg(&pdev->dev, "spmi_add_controller base addr=0x%lx!\n", (long unsigned int)spmi_controller->base);/*lint !e774*/
|
||||
|
||||
/* Get properties from the device tree */
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel",
|
||||
&spmi_controller->channel);/*lint !e838*/
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can not get chanel \n");
|
||||
return -ENODEV; /*lint !e429*/
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, spmi_controller);
|
||||
dev_set_drvdata(&ctrl->dev, spmi_controller);
|
||||
|
||||
spin_lock_init(&spmi_controller->lock);
|
||||
|
||||
ctrl->nr = spmi_controller->channel;
|
||||
ctrl->dev.parent = pdev->dev.parent;
|
||||
ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
|
||||
|
||||
/* Callbacks */
|
||||
ctrl->read_cmd = spmi_read_cmd;
|
||||
ctrl->write_cmd = spmi_write_cmd;
|
||||
|
||||
ret = spmi_controller_add(ctrl);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "spmi_add_controller failed!\n");
|
||||
goto err_add_controller;
|
||||
}
|
||||
err_add_controller:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return ret; /*lint !e429*/
|
||||
}
|
||||
|
||||
static int spmi_del_controller(struct platform_device *pdev)
|
||||
{
|
||||
struct spmi_controller *ctrl = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
spmi_controller_remove(ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id spmi_controller_match_table[] = {
|
||||
{ .compatible = "hisilicon,spmi-controller",
|
||||
},/*lint !e785*/
|
||||
{}/*lint !e785*/
|
||||
};
|
||||
|
||||
static struct platform_driver spmi_controller_driver = {
|
||||
.probe = spmi_controller_probe,
|
||||
.remove = spmi_del_controller,
|
||||
.driver = {
|
||||
.name = SPMI_CONTROLLER_NAME,
|
||||
.owner = THIS_MODULE,/*lint !e64*/
|
||||
.of_match_table = spmi_controller_match_table,
|
||||
},/*lint !e785*/
|
||||
};/*lint !e785*/
|
||||
/*lint -e528 -esym(528,*)*/
|
||||
static int __init spmi_controller_init(void)
|
||||
{
|
||||
return platform_driver_register(&spmi_controller_driver);/*lint !e64*/
|
||||
}
|
||||
postcore_initcall(spmi_controller_init);
|
||||
|
||||
static void __exit spmi_controller_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&spmi_controller_driver);
|
||||
}
|
||||
module_exit(spmi_controller_exit);
|
||||
/*lint -e753 -esym(753,*)*/
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION("1.0");/*lint !e785 !e64 !e528*/
|
||||
MODULE_ALIAS("platform:spmi_controlller");
|
||||
/*lint -e753 +esym(753,*)*/
|
||||
/*lint -e528 +esym(528,*)*/
|
||||
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Header file for device driver Hi6421 PMIC
|
||||
*
|
||||
* Copyright (c) 2013 Linaro Ltd.
|
||||
* Copyright (C) 2011 Hisilicon.
|
||||
*
|
||||
* Guodong Xu <guodong.xu@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __HISI_PMIC_H
|
||||
#define __HISI_PMIC_H
|
||||
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#define HISI_REGS_ENA_PROTECT_TIME (0) /* in microseconds */
|
||||
#define HISI_ECO_MODE_ENABLE (1)
|
||||
#define HISI_ECO_MODE_DISABLE (0)
|
||||
|
||||
typedef int (*pmic_ocp_callback)(char *);
|
||||
extern int hisi_pmic_special_ocp_register(char *power_name, pmic_ocp_callback handler);
|
||||
|
||||
struct irq_mask_info {
|
||||
int start_addr;
|
||||
int array;
|
||||
};
|
||||
|
||||
struct irq_info {
|
||||
int start_addr;
|
||||
int array;
|
||||
};
|
||||
|
||||
struct bit_info {
|
||||
int addr;
|
||||
int bit;
|
||||
};
|
||||
|
||||
struct write_lock {
|
||||
int addr;
|
||||
int val;
|
||||
};
|
||||
|
||||
struct hisi_pmic {
|
||||
struct resource *res;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
spinlock_t lock;
|
||||
struct irq_domain *domain;
|
||||
int irq;
|
||||
int gpio;
|
||||
unsigned int *irqs;
|
||||
int irqnum;
|
||||
int irqarray;
|
||||
struct irq_mask_info irq_mask_addr;
|
||||
struct irq_info irq_addr;
|
||||
int irqnum1;
|
||||
int irqarray1;
|
||||
struct irq_mask_info irq_mask_addr1;
|
||||
struct irq_info irq_addr1;
|
||||
struct write_lock normal_lock;
|
||||
struct write_lock debug_lock;
|
||||
};
|
||||
|
||||
/* 0:disable; 1:enable */
|
||||
unsigned int get_uv_mntn_status(void);
|
||||
void clear_uv_mntn_resered_reg_bit(void);
|
||||
void set_uv_mntn_resered_reg_bit(void);
|
||||
|
||||
#if defined(CONFIG_HISI_PMIC) || defined(CONFIG_HISI_PMIC_PMU_SPMI)
|
||||
/* Register Access Helpers */
|
||||
u32 hisi_pmic_read(struct hisi_pmic *pmic, int reg);
|
||||
void hisi_pmic_write(struct hisi_pmic *pmic, int reg, u32 val);
|
||||
void hisi_pmic_rmw(struct hisi_pmic *pmic, int reg, u32 mask, u32 bits);
|
||||
unsigned int hisi_pmic_reg_read(int addr);
|
||||
void hisi_pmic_reg_write(int addr, int val);
|
||||
void hisi_pmic_reg_write_lock(int addr, int val);
|
||||
int hisi_pmic_array_read(int addr, char *buff, unsigned int len);
|
||||
int hisi_pmic_array_write(int addr, char *buff, unsigned int len);
|
||||
extern int hisi_get_pmic_irq_byname(unsigned int pmic_irq_list);
|
||||
extern int hisi_pmic_get_vbus_status(void);
|
||||
#if defined(CONFIG_HISI_DIEID)
|
||||
u32 hisi_pmic_read_sub_pmu(u8 sid ,int reg);
|
||||
void hisi_pmic_write_sub_pmu(u8 sid ,int reg, u32 val);
|
||||
#endif
|
||||
#else
|
||||
static inline u32 hisi_pmic_read(struct hisi_pmic *pmic, int reg) { return 0; }
|
||||
static inline void hisi_pmic_write(struct hisi_pmic *pmic, int reg, u32 val) {}
|
||||
static inline void hisi_pmic_rmw(struct hisi_pmic *pmic, int reg, u32 mask, u32 bits) {}
|
||||
static inline unsigned int hisi_pmic_reg_read(int addr) { return 0; }
|
||||
static inline void hisi_pmic_reg_write(int addr, int val) {}
|
||||
static inline void hisi_pmic_reg_write_lock(int addr, int val) {}
|
||||
static inline int hisi_pmic_array_read(int addr, char *buff, unsigned int len) { return 0; }
|
||||
static inline int hisi_pmic_array_write(int addr, char *buff, unsigned int len) { return 0; }
|
||||
static inline int hisi_get_pmic_irq_byname(unsigned int pmic_irq_list) { return -1; }
|
||||
static inline int hisi_pmic_get_vbus_status(void) { return 1; }
|
||||
static inline u32 hisi_pmic_read_sub_pmu(u8 sid ,int reg) { return 0; }
|
||||
static inline void hisi_pmic_write_sub_pmu(u8 sid ,int reg, u32 val) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HISI_HI6421V500_PMU
|
||||
enum pmic_irq_list {
|
||||
POR_D45MR = 0,
|
||||
VBUS_CONNECT,
|
||||
VBUS_DISCONNECT,
|
||||
ALARMON_R,
|
||||
HOLD_6S,
|
||||
HOLD_1S,
|
||||
POWERKEY_UP,
|
||||
POWERKEY_DOWN,
|
||||
OCP_SCP_R,
|
||||
COUL_R,
|
||||
VSYS_OV,
|
||||
VSYS_UV,
|
||||
VSYS_PWROFF_ABS,
|
||||
VSYS_PWROFF_DEB,
|
||||
THSD_OTMP140,
|
||||
THSD_OTMP125,
|
||||
HRESETN,
|
||||
SIM0_HPD_R = 24,
|
||||
SIM0_HPD_F,
|
||||
SIM0_HPD_H,
|
||||
SIM0_HPD_L,
|
||||
SIM1_HPD_R,
|
||||
SIM1_HPD_F,
|
||||
SIM1_HPD_H,
|
||||
SIM1_HPD_L,
|
||||
PMIC_IRQ_LIST_MAX,
|
||||
};
|
||||
#else
|
||||
enum pmic_irq_list {
|
||||
OTMP = 0,
|
||||
VBUS_CONNECT,
|
||||
VBUS_DISCONNECT,
|
||||
ALARMON_R,
|
||||
HOLD_6S,
|
||||
HOLD_1S,
|
||||
POWERKEY_UP,
|
||||
POWERKEY_DOWN,
|
||||
OCP_SCP_R,
|
||||
COUL_R,
|
||||
SIM0_HPD_R,
|
||||
SIM0_HPD_F,
|
||||
SIM1_HPD_R,
|
||||
SIM1_HPD_F,
|
||||
PMIC_IRQ_LIST_MAX,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HISI_SR_DEBUG
|
||||
extern void get_ip_regulator_state(void);
|
||||
#endif
|
||||
#endif /* __HISI_PMIC_H */
|
||||
|
||||
Reference in New Issue
Block a user