Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
35ed78a0
Commit
35ed78a0
authored
Oct 30, 2017
by
Ulf Hansson
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'omap_hsmmc' into next
parents
0b07194b
ddde0e7d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
668 additions
and
28 deletions
+668
-28
Documentation/devicetree/bindings/mmc/sdhci-omap.txt
Documentation/devicetree/bindings/mmc/sdhci-omap.txt
+16
-0
MAINTAINERS
MAINTAINERS
+6
-0
drivers/mmc/host/Kconfig
drivers/mmc/host/Kconfig
+12
-0
drivers/mmc/host/Makefile
drivers/mmc/host/Makefile
+1
-0
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/omap_hsmmc.c
+8
-25
drivers/mmc/host/sdhci-omap.c
drivers/mmc/host/sdhci-omap.c
+607
-0
drivers/regulator/pbias-regulator.c
drivers/regulator/pbias-regulator.c
+18
-3
No files found.
Documentation/devicetree/bindings/mmc/sdhci-omap.txt
0 → 100644
View file @
35ed78a0
* TI OMAP SDHCI Controller
Refer to mmc.txt for standard MMC bindings.
Required properties:
- compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
- ti,hwmods: Must be "mmc<n>", <n> is controller instance starting 1
Example:
mmc1: mmc@4809c000 {
compatible = "ti,dra7-sdhci";
reg = <0x4809c000 0x400>;
ti,hwmods = "mmc1";
bus-width = <4>;
vmmc-supply = <&vmmc>; /* phandle to regulator node */
};
MAINTAINERS
View file @
35ed78a0
...
...
@@ -12049,6 +12049,12 @@ L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/sdhci-spear.c
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) TI OMAP DRIVER
M: Kishon Vijay Abraham I <kishon@ti.com>
L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/sdhci-omap.c
SECURE ENCRYPTING DEVICE (SED) OPAL DRIVER
M: Scott Bauer <scott.bauer@intel.com>
M: Jonathan Derrick <jonathan.derrick@intel.com>
...
...
drivers/mmc/host/Kconfig
View file @
35ed78a0
...
...
@@ -899,3 +899,15 @@ config MMC_SDHCI_XENON
This selects Marvell Xenon eMMC/SD/SDIO SDHCI.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_OMAP
tristate "TI SDHCI Controller Support"
depends on MMC_SDHCI_PLTFM && OF
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in TI's DRA7 SOCs. The controller supports
SD/MMC/SDIO devices.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
drivers/mmc/host/Makefile
View file @
35ed78a0
...
...
@@ -89,6 +89,7 @@ obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST)
+=
sdhci-st.o
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)
+=
sdhci-pic32.o
obj-$(CONFIG_MMC_SDHCI_BRCMSTB)
+=
sdhci-brcmstb.o
obj-$(CONFIG_MMC_SDHCI_OMAP)
+=
sdhci-omap.o
ifeq
($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc
+=
-DDEBUG
...
...
drivers/mmc/host/omap_hsmmc.c
View file @
35ed78a0
...
...
@@ -147,10 +147,6 @@
#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
#define VDD_1V8 1800000
/* 180000 uV */
#define VDD_3V0 3000000
/* 300000 uV */
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
...
...
@@ -308,8 +304,7 @@ static int omap_hsmmc_disable_supply(struct mmc_host *mmc)
return
ret
;
}
static
int
omap_hsmmc_set_pbias
(
struct
omap_hsmmc_host
*
host
,
bool
power_on
,
int
vdd
)
static
int
omap_hsmmc_set_pbias
(
struct
omap_hsmmc_host
*
host
,
bool
power_on
)
{
int
ret
;
...
...
@@ -317,17 +312,6 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
return
0
;
if
(
power_on
)
{
if
(
vdd
<=
VDD_165_195
)
ret
=
regulator_set_voltage
(
host
->
pbias
,
VDD_1V8
,
VDD_1V8
);
else
ret
=
regulator_set_voltage
(
host
->
pbias
,
VDD_3V0
,
VDD_3V0
);
if
(
ret
<
0
)
{
dev_err
(
host
->
dev
,
"pbias set voltage fail
\n
"
);
return
ret
;
}
if
(
host
->
pbias_enabled
==
0
)
{
ret
=
regulator_enable
(
host
->
pbias
);
if
(
ret
)
{
...
...
@@ -350,8 +334,7 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
return
0
;
}
static
int
omap_hsmmc_set_power
(
struct
omap_hsmmc_host
*
host
,
int
power_on
,
int
vdd
)
static
int
omap_hsmmc_set_power
(
struct
omap_hsmmc_host
*
host
,
int
power_on
)
{
struct
mmc_host
*
mmc
=
host
->
mmc
;
int
ret
=
0
;
...
...
@@ -363,7 +346,7 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
if
(
IS_ERR
(
mmc
->
supply
.
vmmc
))
return
0
;
ret
=
omap_hsmmc_set_pbias
(
host
,
false
,
0
);
ret
=
omap_hsmmc_set_pbias
(
host
,
false
);
if
(
ret
)
return
ret
;
...
...
@@ -385,7 +368,7 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
if
(
ret
)
return
ret
;
ret
=
omap_hsmmc_set_pbias
(
host
,
true
,
vdd
);
ret
=
omap_hsmmc_set_pbias
(
host
,
true
);
if
(
ret
)
goto
err_set_voltage
;
}
else
{
...
...
@@ -1220,11 +1203,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
clk_disable_unprepare
(
host
->
dbclk
);
/* Turn the power off */
ret
=
omap_hsmmc_set_power
(
host
,
0
,
0
);
ret
=
omap_hsmmc_set_power
(
host
,
0
);
/* Turn the power ON with given VDD 1.8 or 3.0v */
if
(
!
ret
)
ret
=
omap_hsmmc_set_power
(
host
,
1
,
vdd
);
ret
=
omap_hsmmc_set_power
(
host
,
1
);
if
(
host
->
dbclk
)
clk_prepare_enable
(
host
->
dbclk
);
...
...
@@ -1621,10 +1604,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if
(
ios
->
power_mode
!=
host
->
power_mode
)
{
switch
(
ios
->
power_mode
)
{
case
MMC_POWER_OFF
:
omap_hsmmc_set_power
(
host
,
0
,
0
);
omap_hsmmc_set_power
(
host
,
0
);
break
;
case
MMC_POWER_UP
:
omap_hsmmc_set_power
(
host
,
1
,
ios
->
vdd
);
omap_hsmmc_set_power
(
host
,
1
);
break
;
case
MMC_POWER_ON
:
do_send_init_stream
=
1
;
...
...
drivers/mmc/host/sdhci-omap.c
0 → 100644
View file @
35ed78a0
/**
* SDHCI Controller driver for TI's OMAP SoCs
*
* Copyright (C) 2017 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/delay.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include "sdhci-pltfm.h"
#define SDHCI_OMAP_CON 0x12c
#define CON_DW8 BIT(5)
#define CON_DMA_MASTER BIT(20)
#define CON_INIT BIT(1)
#define CON_OD BIT(0)
#define SDHCI_OMAP_CMD 0x20c
#define SDHCI_OMAP_HCTL 0x228
#define HCTL_SDBP BIT(8)
#define HCTL_SDVS_SHIFT 9
#define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
#define HCTL_SDVS_33 (0x7 << HCTL_SDVS_SHIFT)
#define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT)
#define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT)
#define SDHCI_OMAP_SYSCTL 0x22c
#define SYSCTL_CEN BIT(2)
#define SYSCTL_CLKD_SHIFT 6
#define SYSCTL_CLKD_MASK 0x3ff
#define SDHCI_OMAP_STAT 0x230
#define SDHCI_OMAP_IE 0x234
#define INT_CC_EN BIT(0)
#define SDHCI_OMAP_AC12 0x23c
#define AC12_V1V8_SIGEN BIT(19)
#define SDHCI_OMAP_CAPA 0x240
#define CAPA_VS33 BIT(24)
#define CAPA_VS30 BIT(25)
#define CAPA_VS18 BIT(26)
#define SDHCI_OMAP_TIMEOUT 1
/* 1 msec */
#define SYSCTL_CLKD_MAX 0x3FF
#define IOV_1V8 1800000
/* 180000 uV */
#define IOV_3V0 3000000
/* 300000 uV */
#define IOV_3V3 3300000
/* 330000 uV */
struct
sdhci_omap_data
{
u32
offset
;
};
struct
sdhci_omap_host
{
void
__iomem
*
base
;
struct
device
*
dev
;
struct
regulator
*
pbias
;
bool
pbias_enabled
;
struct
sdhci_host
*
host
;
u8
bus_mode
;
u8
power_mode
;
};
static
inline
u32
sdhci_omap_readl
(
struct
sdhci_omap_host
*
host
,
unsigned
int
offset
)
{
return
readl
(
host
->
base
+
offset
);
}
static
inline
void
sdhci_omap_writel
(
struct
sdhci_omap_host
*
host
,
unsigned
int
offset
,
u32
data
)
{
writel
(
data
,
host
->
base
+
offset
);
}
static
int
sdhci_omap_set_pbias
(
struct
sdhci_omap_host
*
omap_host
,
bool
power_on
,
unsigned
int
iov
)
{
int
ret
;
struct
device
*
dev
=
omap_host
->
dev
;
if
(
IS_ERR
(
omap_host
->
pbias
))
return
0
;
if
(
power_on
)
{
ret
=
regulator_set_voltage
(
omap_host
->
pbias
,
iov
,
iov
);
if
(
ret
)
{
dev_err
(
dev
,
"pbias set voltage failed
\n
"
);
return
ret
;
}
if
(
omap_host
->
pbias_enabled
)
return
0
;
ret
=
regulator_enable
(
omap_host
->
pbias
);
if
(
ret
)
{
dev_err
(
dev
,
"pbias reg enable fail
\n
"
);
return
ret
;
}
omap_host
->
pbias_enabled
=
true
;
}
else
{
if
(
!
omap_host
->
pbias_enabled
)
return
0
;
ret
=
regulator_disable
(
omap_host
->
pbias
);
if
(
ret
)
{
dev_err
(
dev
,
"pbias reg disable fail
\n
"
);
return
ret
;
}
omap_host
->
pbias_enabled
=
false
;
}
return
0
;
}
static
int
sdhci_omap_enable_iov
(
struct
sdhci_omap_host
*
omap_host
,
unsigned
int
iov
)
{
int
ret
;
struct
sdhci_host
*
host
=
omap_host
->
host
;
struct
mmc_host
*
mmc
=
host
->
mmc
;
ret
=
sdhci_omap_set_pbias
(
omap_host
,
false
,
0
);
if
(
ret
)
return
ret
;
if
(
!
IS_ERR
(
mmc
->
supply
.
vqmmc
))
{
ret
=
regulator_set_voltage
(
mmc
->
supply
.
vqmmc
,
iov
,
iov
);
if
(
ret
)
{
dev_err
(
mmc_dev
(
mmc
),
"vqmmc set voltage failed
\n
"
);
return
ret
;
}
}
ret
=
sdhci_omap_set_pbias
(
omap_host
,
true
,
iov
);
if
(
ret
)
return
ret
;
return
0
;
}
static
void
sdhci_omap_conf_bus_power
(
struct
sdhci_omap_host
*
omap_host
,
unsigned
char
signal_voltage
)
{
u32
reg
;
ktime_t
timeout
;
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_HCTL
);
reg
&=
~
HCTL_SDVS_MASK
;
if
(
signal_voltage
==
MMC_SIGNAL_VOLTAGE_330
)
reg
|=
HCTL_SDVS_33
;
else
reg
|=
HCTL_SDVS_18
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_HCTL
,
reg
);
reg
|=
HCTL_SDBP
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_HCTL
,
reg
);
/* wait 1ms */
timeout
=
ktime_add_ms
(
ktime_get
(),
SDHCI_OMAP_TIMEOUT
);
while
(
!
(
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_HCTL
)
&
HCTL_SDBP
))
{
if
(
WARN_ON
(
ktime_after
(
ktime_get
(),
timeout
)))
return
;
usleep_range
(
5
,
10
);
}
}
static
int
sdhci_omap_start_signal_voltage_switch
(
struct
mmc_host
*
mmc
,
struct
mmc_ios
*
ios
)
{
u32
reg
;
int
ret
;
unsigned
int
iov
;
struct
sdhci_host
*
host
=
mmc_priv
(
mmc
);
struct
sdhci_pltfm_host
*
pltfm_host
;
struct
sdhci_omap_host
*
omap_host
;
struct
device
*
dev
;
pltfm_host
=
sdhci_priv
(
host
);
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
dev
=
omap_host
->
dev
;
if
(
ios
->
signal_voltage
==
MMC_SIGNAL_VOLTAGE_330
)
{
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CAPA
);
if
(
!
(
reg
&
CAPA_VS33
))
return
-
EOPNOTSUPP
;
sdhci_omap_conf_bus_power
(
omap_host
,
ios
->
signal_voltage
);
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_AC12
);
reg
&=
~
AC12_V1V8_SIGEN
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_AC12
,
reg
);
iov
=
IOV_3V3
;
}
else
if
(
ios
->
signal_voltage
==
MMC_SIGNAL_VOLTAGE_180
)
{
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CAPA
);
if
(
!
(
reg
&
CAPA_VS18
))
return
-
EOPNOTSUPP
;
sdhci_omap_conf_bus_power
(
omap_host
,
ios
->
signal_voltage
);
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_AC12
);
reg
|=
AC12_V1V8_SIGEN
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_AC12
,
reg
);
iov
=
IOV_1V8
;
}
else
{
return
-
EOPNOTSUPP
;
}
ret
=
sdhci_omap_enable_iov
(
omap_host
,
iov
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to switch IO voltage to %dmV
\n
"
,
iov
);
return
ret
;
}
dev_dbg
(
dev
,
"IO voltage switched to %dmV
\n
"
,
iov
);
return
0
;
}
static
void
sdhci_omap_set_bus_mode
(
struct
sdhci_omap_host
*
omap_host
,
unsigned
int
mode
)
{
u32
reg
;
if
(
omap_host
->
bus_mode
==
mode
)
return
;
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CON
);
if
(
mode
==
MMC_BUSMODE_OPENDRAIN
)
reg
|=
CON_OD
;
else
reg
&=
~
CON_OD
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CON
,
reg
);
omap_host
->
bus_mode
=
mode
;
}
static
void
sdhci_omap_set_ios
(
struct
mmc_host
*
mmc
,
struct
mmc_ios
*
ios
)
{
struct
sdhci_host
*
host
=
mmc_priv
(
mmc
);
struct
sdhci_pltfm_host
*
pltfm_host
;
struct
sdhci_omap_host
*
omap_host
;
pltfm_host
=
sdhci_priv
(
host
);
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
sdhci_omap_set_bus_mode
(
omap_host
,
ios
->
bus_mode
);
sdhci_set_ios
(
mmc
,
ios
);
}
static
u16
sdhci_omap_calc_divisor
(
struct
sdhci_pltfm_host
*
host
,
unsigned
int
clock
)
{
u16
dsor
;
dsor
=
DIV_ROUND_UP
(
clk_get_rate
(
host
->
clk
),
clock
);
if
(
dsor
>
SYSCTL_CLKD_MAX
)
dsor
=
SYSCTL_CLKD_MAX
;
return
dsor
;
}
static
void
sdhci_omap_start_clock
(
struct
sdhci_omap_host
*
omap_host
)
{
u32
reg
;
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_SYSCTL
);
reg
|=
SYSCTL_CEN
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_SYSCTL
,
reg
);
}
static
void
sdhci_omap_stop_clock
(
struct
sdhci_omap_host
*
omap_host
)
{
u32
reg
;
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_SYSCTL
);
reg
&=
~
SYSCTL_CEN
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_SYSCTL
,
reg
);
}
static
void
sdhci_omap_set_clock
(
struct
sdhci_host
*
host
,
unsigned
int
clock
)
{
struct
sdhci_pltfm_host
*
pltfm_host
=
sdhci_priv
(
host
);
struct
sdhci_omap_host
*
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
unsigned
long
clkdiv
;
sdhci_omap_stop_clock
(
omap_host
);
if
(
!
clock
)
return
;
clkdiv
=
sdhci_omap_calc_divisor
(
pltfm_host
,
clock
);
clkdiv
=
(
clkdiv
&
SYSCTL_CLKD_MASK
)
<<
SYSCTL_CLKD_SHIFT
;
sdhci_enable_clk
(
host
,
clkdiv
);
sdhci_omap_start_clock
(
omap_host
);
}
static
void
sdhci_omap_set_power
(
struct
sdhci_host
*
host
,
unsigned
char
mode
,
unsigned
short
vdd
)
{
struct
mmc_host
*
mmc
=
host
->
mmc
;
mmc_regulator_set_ocr
(
mmc
,
mmc
->
supply
.
vmmc
,
vdd
);
}
static
int
sdhci_omap_enable_dma
(
struct
sdhci_host
*
host
)
{
u32
reg
;
struct
sdhci_pltfm_host
*
pltfm_host
=
sdhci_priv
(
host
);
struct
sdhci_omap_host
*
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CON
);
reg
|=
CON_DMA_MASTER
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CON
,
reg
);
return
0
;
}
static
unsigned
int
sdhci_omap_get_min_clock
(
struct
sdhci_host
*
host
)
{
struct
sdhci_pltfm_host
*
pltfm_host
=
sdhci_priv
(
host
);
return
clk_get_rate
(
pltfm_host
->
clk
)
/
SYSCTL_CLKD_MAX
;
}
static
void
sdhci_omap_set_bus_width
(
struct
sdhci_host
*
host
,
int
width
)
{
struct
sdhci_pltfm_host
*
pltfm_host
=
sdhci_priv
(
host
);
struct
sdhci_omap_host
*
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
u32
reg
;
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CON
);
if
(
width
==
MMC_BUS_WIDTH_8
)
reg
|=
CON_DW8
;
else
reg
&=
~
CON_DW8
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CON
,
reg
);
sdhci_set_bus_width
(
host
,
width
);
}
static
void
sdhci_omap_init_74_clocks
(
struct
sdhci_host
*
host
,
u8
power_mode
)
{
u32
reg
;
ktime_t
timeout
;
struct
sdhci_pltfm_host
*
pltfm_host
=
sdhci_priv
(
host
);
struct
sdhci_omap_host
*
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
if
(
omap_host
->
power_mode
==
power_mode
)
return
;
if
(
power_mode
!=
MMC_POWER_ON
)
return
;
disable_irq
(
host
->
irq
);
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CON
);
reg
|=
CON_INIT
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CON
,
reg
);
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CMD
,
0x0
);
/* wait 1ms */
timeout
=
ktime_add_ms
(
ktime_get
(),
SDHCI_OMAP_TIMEOUT
);
while
(
!
(
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_STAT
)
&
INT_CC_EN
))
{
if
(
WARN_ON
(
ktime_after
(
ktime_get
(),
timeout
)))
return
;
usleep_range
(
5
,
10
);
}
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CON
);
reg
&=
~
CON_INIT
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CON
,
reg
);
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_STAT
,
INT_CC_EN
);
enable_irq
(
host
->
irq
);
omap_host
->
power_mode
=
power_mode
;
}
static
struct
sdhci_ops
sdhci_omap_ops
=
{
.
set_clock
=
sdhci_omap_set_clock
,
.
set_power
=
sdhci_omap_set_power
,
.
enable_dma
=
sdhci_omap_enable_dma
,
.
get_max_clock
=
sdhci_pltfm_clk_get_max_clock
,
.
get_min_clock
=
sdhci_omap_get_min_clock
,
.
set_bus_width
=
sdhci_omap_set_bus_width
,
.
platform_send_init_74_clocks
=
sdhci_omap_init_74_clocks
,
.
reset
=
sdhci_reset
,
.
set_uhs_signaling
=
sdhci_set_uhs_signaling
,
};
static
int
sdhci_omap_set_capabilities
(
struct
sdhci_omap_host
*
omap_host
)
{
u32
reg
;
int
ret
=
0
;
struct
device
*
dev
=
omap_host
->
dev
;
struct
regulator
*
vqmmc
;
vqmmc
=
regulator_get
(
dev
,
"vqmmc"
);
if
(
IS_ERR
(
vqmmc
))
{
ret
=
PTR_ERR
(
vqmmc
);
goto
reg_put
;
}
/* voltage capabilities might be set by boot loader, clear it */
reg
=
sdhci_omap_readl
(
omap_host
,
SDHCI_OMAP_CAPA
);
reg
&=
~
(
CAPA_VS18
|
CAPA_VS30
|
CAPA_VS33
);
if
(
regulator_is_supported_voltage
(
vqmmc
,
IOV_3V3
,
IOV_3V3
))
reg
|=
CAPA_VS33
;
if
(
regulator_is_supported_voltage
(
vqmmc
,
IOV_1V8
,
IOV_1V8
))
reg
|=
CAPA_VS18
;
sdhci_omap_writel
(
omap_host
,
SDHCI_OMAP_CAPA
,
reg
);
reg_put:
regulator_put
(
vqmmc
);
return
ret
;
}
static
const
struct
sdhci_pltfm_data
sdhci_omap_pdata
=
{
.
quirks
=
SDHCI_QUIRK_BROKEN_CARD_DETECTION
|
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
|
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
|
SDHCI_QUIRK_NO_HISPD_BIT
|
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
,
.
quirks2
=
SDHCI_QUIRK2_NO_1_8_V
|
SDHCI_QUIRK2_ACMD23_BROKEN
|
SDHCI_QUIRK2_RSP_136_HAS_CRC
,
.
ops
=
&
sdhci_omap_ops
,
};
static
const
struct
sdhci_omap_data
dra7_data
=
{
.
offset
=
0x200
,
};
static
const
struct
of_device_id
omap_sdhci_match
[]
=
{
{
.
compatible
=
"ti,dra7-sdhci"
,
.
data
=
&
dra7_data
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
omap_sdhci_match
);
static
int
sdhci_omap_probe
(
struct
platform_device
*
pdev
)
{
int
ret
;
u32
offset
;
struct
device
*
dev
=
&
pdev
->
dev
;
struct
sdhci_host
*
host
;
struct
sdhci_pltfm_host
*
pltfm_host
;
struct
sdhci_omap_host
*
omap_host
;
struct
mmc_host
*
mmc
;
const
struct
of_device_id
*
match
;
struct
sdhci_omap_data
*
data
;
match
=
of_match_device
(
omap_sdhci_match
,
dev
);
if
(
!
match
)
return
-
EINVAL
;
data
=
(
struct
sdhci_omap_data
*
)
match
->
data
;
if
(
!
data
)
{
dev_err
(
dev
,
"no sdhci omap data
\n
"
);
return
-
EINVAL
;
}
offset
=
data
->
offset
;
host
=
sdhci_pltfm_init
(
pdev
,
&
sdhci_omap_pdata
,
sizeof
(
*
omap_host
));
if
(
IS_ERR
(
host
))
{
dev_err
(
dev
,
"Failed sdhci_pltfm_init
\n
"
);
return
PTR_ERR
(
host
);
}
pltfm_host
=
sdhci_priv
(
host
);
omap_host
=
sdhci_pltfm_priv
(
pltfm_host
);
omap_host
->
host
=
host
;
omap_host
->
base
=
host
->
ioaddr
;
omap_host
->
dev
=
dev
;
host
->
ioaddr
+=
offset
;
mmc
=
host
->
mmc
;
ret
=
mmc_of_parse
(
mmc
);
if
(
ret
)
goto
err_pltfm_free
;
pltfm_host
->
clk
=
devm_clk_get
(
dev
,
"fck"
);
if
(
IS_ERR
(
pltfm_host
->
clk
))
{
ret
=
PTR_ERR
(
pltfm_host
->
clk
);
goto
err_pltfm_free
;
}
ret
=
clk_set_rate
(
pltfm_host
->
clk
,
mmc
->
f_max
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to set clock to %d
\n
"
,
mmc
->
f_max
);
goto
err_pltfm_free
;
}
omap_host
->
pbias
=
devm_regulator_get_optional
(
dev
,
"pbias"
);
if
(
IS_ERR
(
omap_host
->
pbias
))
{
ret
=
PTR_ERR
(
omap_host
->
pbias
);
if
(
ret
!=
-
ENODEV
)
goto
err_pltfm_free
;
dev_dbg
(
dev
,
"unable to get pbias regulator %d
\n
"
,
ret
);
}
omap_host
->
pbias_enabled
=
false
;
/*
* omap_device_pm_domain has callbacks to enable the main
* functional clock, interface clock and also configure the
* SYSCONFIG register of omap devices. The callback will be invoked
* as part of pm_runtime_get_sync.
*/
pm_runtime_enable
(
dev
);
ret
=
pm_runtime_get_sync
(
dev
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"pm_runtime_get_sync failed
\n
"
);
pm_runtime_put_noidle
(
dev
);
goto
err_rpm_disable
;
}
ret
=
sdhci_omap_set_capabilities
(
omap_host
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to set system capabilities
\n
"
);
goto
err_put_sync
;
}
host
->
mmc_host_ops
.
get_ro
=
mmc_gpio_get_ro
;
host
->
mmc_host_ops
.
start_signal_voltage_switch
=
sdhci_omap_start_signal_voltage_switch
;
host
->
mmc_host_ops
.
set_ios
=
sdhci_omap_set_ios
;
sdhci_read_caps
(
host
);
host
->
caps
|=
SDHCI_CAN_DO_ADMA2
;
ret
=
sdhci_add_host
(
host
);
if
(
ret
)
goto
err_put_sync
;
return
0
;
err_put_sync:
pm_runtime_put_sync
(
dev
);
err_rpm_disable:
pm_runtime_disable
(
dev
);
err_pltfm_free:
sdhci_pltfm_free
(
pdev
);
return
ret
;
}
static
int
sdhci_omap_remove
(
struct
platform_device
*
pdev
)
{
struct
device
*
dev
=
&
pdev
->
dev
;
struct
sdhci_host
*
host
=
platform_get_drvdata
(
pdev
);
sdhci_remove_host
(
host
,
true
);
pm_runtime_put_sync
(
dev
);
pm_runtime_disable
(
dev
);
sdhci_pltfm_free
(
pdev
);
return
0
;
}
static
struct
platform_driver
sdhci_omap_driver
=
{
.
probe
=
sdhci_omap_probe
,
.
remove
=
sdhci_omap_remove
,
.
driver
=
{
.
name
=
"sdhci-omap"
,
.
of_match_table
=
omap_sdhci_match
,
},
};
module_platform_driver
(
sdhci_omap_driver
);
MODULE_DESCRIPTION
(
"SDHCI driver for OMAP SoCs"
);
MODULE_AUTHOR
(
"Texas Instruments Inc."
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_ALIAS
(
"platform:sdhci_omap"
);
drivers/regulator/pbias-regulator.c
View file @
35ed78a0
...
...
@@ -34,6 +34,8 @@ struct pbias_reg_info {
u32
vmode
;
unsigned
int
enable_time
;
char
*
name
;
const
unsigned
int
*
pbias_volt_table
;
int
n_voltages
;
};
struct
pbias_regulator_data
{
...
...
@@ -49,11 +51,16 @@ struct pbias_of_data {
unsigned
int
offset
;
};
static
const
unsigned
int
pbias_volt_table
[]
=
{
static
const
unsigned
int
pbias_volt_table
_3_0V
[]
=
{
1800000
,
3000000
};
static
const
unsigned
int
pbias_volt_table_3_3V
[]
=
{
1800000
,
3300000
};
static
const
struct
regulator_ops
pbias_regulator_voltage_ops
=
{
.
list_voltage
=
regulator_list_voltage_table
,
.
get_voltage_sel
=
regulator_get_voltage_sel_regmap
,
...
...
@@ -69,6 +76,8 @@ static const struct pbias_reg_info pbias_mmc_omap2430 = {
.
vmode
=
BIT
(
0
),
.
disable_val
=
0
,
.
enable_time
=
100
,
.
pbias_volt_table
=
pbias_volt_table_3_0V
,
.
n_voltages
=
2
,
.
name
=
"pbias_mmc_omap2430"
};
...
...
@@ -77,6 +86,8 @@ static const struct pbias_reg_info pbias_sim_omap3 = {
.
enable_mask
=
BIT
(
9
),
.
vmode
=
BIT
(
8
),
.
enable_time
=
100
,
.
pbias_volt_table
=
pbias_volt_table_3_0V
,
.
n_voltages
=
2
,
.
name
=
"pbias_sim_omap3"
};
...
...
@@ -86,6 +97,8 @@ static const struct pbias_reg_info pbias_mmc_omap4 = {
.
disable_val
=
BIT
(
25
),
.
vmode
=
BIT
(
21
),
.
enable_time
=
100
,
.
pbias_volt_table
=
pbias_volt_table_3_0V
,
.
n_voltages
=
2
,
.
name
=
"pbias_mmc_omap4"
};
...
...
@@ -95,6 +108,8 @@ static const struct pbias_reg_info pbias_mmc_omap5 = {
.
disable_val
=
BIT
(
25
),
.
vmode
=
BIT
(
21
),
.
enable_time
=
100
,
.
pbias_volt_table
=
pbias_volt_table_3_3V
,
.
n_voltages
=
2
,
.
name
=
"pbias_mmc_omap5"
};
...
...
@@ -199,8 +214,8 @@ static int pbias_regulator_probe(struct platform_device *pdev)
drvdata
[
data_idx
].
desc
.
owner
=
THIS_MODULE
;
drvdata
[
data_idx
].
desc
.
type
=
REGULATOR_VOLTAGE
;
drvdata
[
data_idx
].
desc
.
ops
=
&
pbias_regulator_voltage_ops
;
drvdata
[
data_idx
].
desc
.
volt_table
=
pbias_volt_table
;
drvdata
[
data_idx
].
desc
.
n_voltages
=
2
;
drvdata
[
data_idx
].
desc
.
volt_table
=
info
->
pbias_volt_table
;
drvdata
[
data_idx
].
desc
.
n_voltages
=
info
->
n_voltages
;
drvdata
[
data_idx
].
desc
.
enable_time
=
info
->
enable_time
;
drvdata
[
data_idx
].
desc
.
vsel_reg
=
offset
;
drvdata
[
data_idx
].
desc
.
vsel_mask
=
info
->
vmode
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment