Merge branch 'fifteen' of ssh://git.ipfire.org/pub/git/ipfire-2.x into fifteen

This commit is contained in:
Arne Fitzenreiter
2013-12-23 22:27:58 +01:00
31 changed files with 10546 additions and 1558 deletions

View File

@@ -28,6 +28,7 @@
###############################################################################
require '/var/ipfire/general-functions.pl';
require "${General::swroot}/lang.pl";
use Socket;
use File::Path;
@@ -66,6 +67,7 @@ my @active= ('Aktiv', 'aktiv', 'Active', 'Activo', 'Actif', 'Actief', 'Aktywne',
&General::readhash("${General::swroot}/ovpn/settings", \%ovpnSettings);
&General::readhash($outfwsettings,\%outsettings);
&General::readhash("${General::swroot}/ethernet/settings", \%ownnet);
#ONLY RUN if /var/ipfire/outgoing exists
if ( -d "/var/ipfire/outgoing"){
&process_groups;
@@ -164,6 +166,7 @@ sub new_hostgrp
my $name; #"converted"
my $name2;
my $name3; #custom host/custom net
my $mac2;
foreach my $adr (@hostarray){
if($run eq 'ip'){
my ($ip,$type) = split(",",$adr);
@@ -180,17 +183,11 @@ sub new_hostgrp
$hosts{$key}[1] = $type;
$hosts{$key}[2] = $ip;
$hosts{$key}[3] = '';
$hosts{$key}[4] = 1;
print LOG "->Host (IP) $ip added to custom hosts\n"
}else{
print LOG "->Host (IP) $ip already exists in custom hosts\n";
$name="host ";
$name2=$name.$ippart;
foreach my $key (sort keys %hosts){
if($hosts{$key}[0] eq $name2){
$hosts{$key}[4]++;
}
}
$name="host ";
$name2=$name.$ippart;
$name3="Custom Host";
@@ -228,18 +225,12 @@ sub new_hostgrp
$nets{$netkey}[1] = $ippart;
$nets{$netkey}[2] = $subnet;
$nets{$netkey}[3] = '';
$nets{$netkey}[4] = 1;
print LOG "->Network $ippart/$subnet added to custom networks\n";
}
}else{
print LOG "Network $ippart already exists in custom networks\n";
$name="net ";
$name2=$name.$ippart;
foreach my $key (sort keys %nets){
if($nets{$key}[0] eq $name2){
$nets{$key}[4]++;
}
}
$name="net ";
$name2=$name.$ippart;
$name3="Custom Network";
@@ -251,35 +242,29 @@ sub new_hostgrp
$groups{$grpkey}[1] = '';
$groups{$grpkey}[2] = $name2;
$groups{$grpkey}[3] = $name3;
$groups{$grpkey}[4] = 0;
print LOG "->$name2 added to group $grp\n";
}
}elsif($run eq 'mac'){
#MACRUN
my ($mac,$type) = split(",",$adr);
my ($mac,$type) = split(",",$adr);
print LOG "Processing HOST (MAC) $mac\n";
if(!&check_host($mac)){
my $key = &General::findhasharraykey(\%hosts);
my $key = &General::findhasharraykey(\%hosts);
$name="host ";
$name2=$name.$mac;
$mac2=$mac;
$mac2 =~ s/:/-/g;
$name2=$name.$mac2;
$name3="Custom Host";
$hosts{$key}[0] = $name2;
$hosts{$key}[1] = $type;
$hosts{$key}[2] = $mac;
$hosts{$key}[3] = '';
$hosts{$key}[4] = 1;
print LOG "->Host (MAC) $mac added to custom hosts\n";
}else{
$mac2=mac;
$mac2 =~ s/:/-/g;
print LOG "->Host (MAC) $mac already exists in custom hosts \n";
$name="host ";
$name2=$name.$mac;
foreach my $key (sort keys %hosts){
if($hosts{$key}[0] eq $name2){
$hosts{$key}[4]++;
}
}
$name="host ";
$name2=$name.$mac;
$name2=$name.$mac2;
$name3="Custom Host";
}
if($name2 && !&check_grp($grp,$name2)){
@@ -288,7 +273,6 @@ sub new_hostgrp
$groups{$grpkey}[1] = '';
$groups{$grpkey}[2] = $name2;
$groups{$grpkey}[3] = $name3;
$groups{$grpkey}[4] = 0;
print LOG "->$name2 added to group $grp\n";
}
}
@@ -361,6 +345,8 @@ sub process_rules
my @lines = <DATEI>;
foreach my $rule (@lines)
{
&General::readhasharray($fwdfwconfig,\%fwconfig);
&General::readhasharray($outfwconfig,\%fwconfigout);
my $now=localtime;
chomp($rule);
$port='';
@@ -468,7 +454,7 @@ sub process_rules
}
############################################################
#destinationpart
if($configline[7] ne ''){
if($configline[7] ne '' && $configline[7] ne '0.0.0.0'){
my $address=&check_ip($configline[7]);
if($address){
my ($dip,$dsub) = split("/",$address);
@@ -523,8 +509,6 @@ sub process_rules
}else{
print LOG "-> Rule not converted because not for Firewall mode $outsettings{'POLICY'} (we are only converting for actual mode)\n";
}
&General::readhasharray($fwdfwconfig,\%fwconfig);
&General::readhasharray($outfwconfig,\%fwconfigout);
my $check;
my $chain;
foreach my $protocol (@prot){
@@ -535,31 +519,18 @@ sub process_rules
$chain='FORWARDFW';
}
$protocol=uc($protocol);
print LOG "$now -> Converted: $action,$chain,$active,$grp1,$source,$grp2,$target,,,,,$useport,$protocol,,$grp3,$port,$remark,$log,$time,$time_mon,$time_tue,$time_wed,$time_thu,$time_fri,$time_sat,$time_sun,$time_from,$time_to\n";
print LOG "$now -> Converted: $action,$chain,$active,$grp1,$source,$grp2,$target,,$protocol,,,$useport,,,$grp3,$port,$remark,$log,$time,$time_mon,$time_tue,$time_wed,$time_thu,$time_fri,$time_sat,$time_sun,$time_from,$time_to\n";
#Put rules into system....
###########################
#check for double rules
foreach my $key (sort keys %fwconfig){
if("$action,$chain,$active,$grp1,$source,$grp2,$target,,,,,$useport,$protocol,,$grp3,$port,$remark,$log,$time,$time_mon,$time_tue,$time_wed,$time_thu,$time_fri,$time_sat,$time_sun,$time_from,$time_to"
eq "$fwconfig{$key}[0],$fwconfig{$key}[1],$fwconfig{$key}[2],$fwconfig{$key}[3],$fwconfig{$key}[4],$fwconfig{$key}[5],$fwconfig{$key}[6],,,,,$fwconfig{$key}[11],$fwconfig{$key}[12],,$fwconfig{$key}[14],$fwconfig{$key}[15],$fwconfig{$key}[16],$fwconfig{$key}[17],$fwconfig{$key}[18],$fwconfig{$key}[19],$fwconfig{$key}[20],$fwconfig{$key}[21],$fwconfig{$key}[22],$fwconfig{$key}[23],$fwconfig{$key}[24],$fwconfig{$key}[25],$fwconfig{$key}[26],$fwconfig{$key}[27]"){
if("$action,$chain,$active,$grp1,$source,$grp2,$target,$protocol,$useport,$grp3,$port,$remark,$log,$time,$time_mon,$time_tue,$time_wed,$time_thu,$time_fri,$time_sat,$time_sun,$time_from,$time_to"
eq "$fwconfig{$key}[0],$fwconfig{$key}[1],$fwconfig{$key}[2],$fwconfig{$key}[3],$fwconfig{$key}[4],$fwconfig{$key}[5],$fwconfig{$key}[6],$fwconfig{$key}[8],$fwconfig{$key}[11],$fwconfig{$key}[14],$fwconfig{$key}[15],$fwconfig{$key}[16],$fwconfig{$key}[17],$fwconfig{$key}[18],$fwconfig{$key}[19],$fwconfig{$key}[20],$fwconfig{$key}[21],$fwconfig{$key}[22],$fwconfig{$key}[23],$fwconfig{$key}[24],$fwconfig{$key}[25],$fwconfig{$key}[26],$fwconfig{$key}[27]"){
$check='on';
next;
}
}
if($check ne 'on'){
#increase groupcounter
my $check1;
if($grp1 eq 'cust_grp_src'){
foreach my $key (sort keys %groups){
if($groups{$key}[0] eq $source){
$groups{$key}[4]++;
$check1='on';
}
}
if($check1 eq 'on'){
&General::writehasharray($configgroups,\%groups);
}
}
if ($chain eq 'FORWARDFW'){
my $key = &General::findhasharraykey(\%fwconfig);
$fwconfig{$key}[0] = $action;
@@ -569,8 +540,8 @@ sub process_rules
$fwconfig{$key}[4] = $source;
$fwconfig{$key}[5] = $grp2;
$fwconfig{$key}[6] = $target;
$fwconfig{$key}[8] = $protocol;
$fwconfig{$key}[11] = $useport;
$fwconfig{$key}[12] = $protocol;
$fwconfig{$key}[14] = $grp3;
$fwconfig{$key}[15] = $port;
$fwconfig{$key}[16] = $remark;
@@ -589,6 +560,7 @@ sub process_rules
$fwconfig{$key}[29] = 'ALL';
$fwconfig{$key}[30] = '';
$fwconfig{$key}[31] = 'dnat';
&General::writehasharray($fwdfwconfig,\%fwconfig);
}else{
my $key = &General::findhasharraykey(\%fwconfigout);
$fwconfigout{$key}[0] = $action;
@@ -598,8 +570,8 @@ sub process_rules
$fwconfigout{$key}[4] = $source;
$fwconfigout{$key}[5] = $grp2;
$fwconfigout{$key}[6] = $target;
$fwconfigout{$key}[8] = $protocol;
$fwconfigout{$key}[11] = $useport;
$fwconfigout{$key}[12] = $protocol;
$fwconfigout{$key}[14] = $grp3;
$fwconfigout{$key}[15] = $port;
$fwconfigout{$key}[16] = $remark;
@@ -618,9 +590,8 @@ sub process_rules
$fwconfigout{$key}[29] = 'ALL';
$fwconfigout{$key}[30] = '';
$fwconfigout{$key}[31] = 'dnat';
&General::writehasharray($outfwconfig,\%fwconfigout);
}
&General::writehasharray($fwdfwconfig,\%fwconfig);
&General::writehasharray($outfwconfig,\%fwconfigout);
}
}
@prot=();
@@ -681,7 +652,6 @@ sub build_ovpn_grp
$nets{$netkey}[1] = $net;
$nets{$netkey}[2] = $subnet;
$nets{$netkey}[3] = '';
$nets{$netkey}[4] = 1;
print LOG "$now ->added $name2 $net/$subnet to customnetworks\n";
}else{
print LOG "-> Custom Network with same IP already exist \"$net/$subnet\" (you can ignore this, if this run was manual from shell)\n";
@@ -692,7 +662,6 @@ sub build_ovpn_grp
$groups{$grpkey}[1] = '';
$groups{$grpkey}[2] = $name2;
$groups{$grpkey}[3] = "Custom Network";
$groups{$grpkey}[4] = 0;
print LOG "$now ->added $name2 to customgroup ovpn\n";
}
$name2='';

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,14 @@
etc/system-release
etc/issue
etc/rc.d/init.d/network
srv/web/ipfire/cgi-bin/credits.cgi
srv/web/ipfire/cgi-bin/index.cgi
srv/web/ipfire/cgi-bin/netinternal.cgi
srv/web/ipfire/cgi-bin/ovpnmain.cgi
srv/web/ipfire/cgi-bin/proxy.cgi
srv/web/ipfire/cgi-bin/upnp.cgi
srv/web/ipfire/cgi-bin/speed.cgi
srv/web/ipfire/cgi-bin/url-filter.cgi
srv/web/ipfire/cgi-bin/vpnmain.cgi
srv/web/ipfire/html/themes/ipfire/include/functions.pl
srv/web/ipfire/html/themes/maniac/include/functions.pl

View File

@@ -52,11 +52,16 @@ rm -f /etc/rc.d/init.d/networking/red.up/26-xtaccess
# Remove old CGI files
rm -f /srv/web/ipfire/cgi-bin/{dmzholes,outgoingfw,portfw,xtaccess}.cgi
# Generate chains for new firewall
/sbin/iptables -N INPUTFW
/sbin/iptables -N FORWARDFW
/sbin/iptables -N OUTGOINGFW
# Convert firewall configuration
/usr/bin/convert-xtaccess
/usr/bin/convert-outgoingfw
/usr/bin/convert-portfw
/usr/bin/convert-dmz
/usr/sbin/convert-xtaccess
/usr/sbin/convert-outgoingfw
/usr/sbin/convert-portfw
/usr/sbin/convert-dmz
# Remove old firewall configuration files
rm -rf /var/ipfire/{dmzholes,portfw,outgoing,xtaccess}

View File

@@ -68,6 +68,8 @@ Christian Schmidt
(<a href='mailto:christian.schmidt\@ipfire.org'>christian.schmidt\@ipfire.org</a>) - Vice Project Leader <br />
Stefan Schantl
(<a href='mailto:stefan.schantl\@ipfire.org'>stefan.schantl\@ipfire.org</a>)<br />
Alexander Marx
(<a href='mailto:alexander.marx\@ipfire.org'>alexander.marx\@ipfire.org</a>)<br />
Heiner Schmeling
(<a href='mailto:heiner.schmeling\@ipfire.org'>heiner.schmeling\@ipfire.org</a>)<br />
Ronald Wiesinger

View File

@@ -165,6 +165,35 @@ ifeq "$(KCFG)" "-multi"
# cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.2-0002-panda-i2c.patch
# cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.2-panda-reboot.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10-smsc95xx-add_mac_addr_param.patch
# Patchset for Wandboard.
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/dts/0001-imx6qdl-wandboard-dts-backport.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/dts/0002-ARM-dts-imx6qdl-wandboard-add-gpio-lines-to-wandboar.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/dts/0003-ARM-dts-imx6qdl-wandboard-Add-support-for-i2c1.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/dts/0004-ARM-dts-wandboard-add-binding-for-wand-rfkill-driver.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/dts/0005-ARM-dts-imx6qdl-add-pcie-device-node.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0001-i2c-imx-retry-on-NAK.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0002-i.MX6-Wandboard-add-CKO1-clock-output.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0003-thermal-add-imx-thermal-driver-support.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0004-ARM-i.MX6-Wandboard-add-wifi-bt-rfkill-driver.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0005-Add-IMX6Q-AHCI-support.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0006-imx-Add-IMX53-AHCI-support.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0007-imx6-enable-sata-clk-if-SATA_AHCI_PLATFORM.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0008-ARM-imx6q-update-the-sata-bits-definitions-of-gpr13.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0009-ahci_imx-add-ahci-sata-support-on-imx-platforms.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0010-ahci_imx-depend-on-CONFIG_MFD_SYSCON.patch
cd $(DIR_APP) && patch -Np0 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0011-add-pcie-designware.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0012-pcie-backport-fixes.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0013-of-pci-Provide-support-for-parsing-PCI-DT-ranges-pro.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0014-ARM-imx6q-Add-PCIe-bits-to-GPR-syscon-definition.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0015-PCI-imx6-Add-support-for-i.MX6-PCIe-controller.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0016-imx6-pci-tweaks.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0017-ARM-imx-Add-LVDS-general-purpose-clocks-to-i.MX6Q.patch
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/wandboard/imx/0018-ARM-imx6q-clock-and-Kconfig-update-for-PCIe-support.patch
# Patchset for Compulab Utilite.
cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/kernel/utilite/linux-3.10-compulab-utilite-support.patch
endif
ifeq "$(KCFG)" "-rpi"

View File

@@ -99,7 +99,7 @@ iptables_init() {
# Block OpenVPN transfer networks
/sbin/iptables -N OVPNBLOCK
for i in INPUT FORWARD OUTPUT; do
for i in INPUT FORWARD; do
/sbin/iptables -A ${i} -j OVPNBLOCK
done

View File

@@ -0,0 +1,96 @@
Add initial support for cm-fx6 module.
cm-fx6 is a module based on mx6q SoC with the following features:
- Up to 4GB of DDR3
- 1 LCD/DVI output port
- 1 HDMI output port
- 2 LVDS LCD ports
- Gigabit Ethernet
- Analog Audio
- CAN
- SATA
- NAND
- PCIE
This patch allows to boot up the module, configures the serial console,
the Ethernet adapter and the heartbeat led.
cm-fx6 is embedded inside the Utilite computer.
Signed-off-by: Valentin Raevsky <valentin@compulab.co.il>
Signed-off-by: Igor Grinberg <grinberg@compulab.co.il>
---
Shawn, can this still be applied for 3.13 ?
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index f0895c5..7521a34 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -117,6 +117,7 @@ dtb-$(CONFIG_ARCH_MXC) += \
imx6dl-sabresd.dtb \
imx6dl-wandboard.dtb \
imx6q-arm2.dtb \
+ imx6q-cm-fx6.dtb \
imx6q-sabreauto.dtb \
imx6q-sabrelite.dtb \
imx6q-sabresd.dtb \
diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts
new file mode 100644
index 0000000..2419751
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 CompuLab Ltd.
+ *
+ * Author: Valentin Raevsky <valentin@compulab.co.il>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+
+/ {
+ model = "CompuLab CM-FX6";
+ compatible = "compulab,cm-fx6", "fsl,imx6q";
+
+ memory {
+ reg = <0x10000000 0x80000000>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ heartbeat-led {
+ label = "Heartbeat";
+ gpios = <&gpio2 31 0>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet_1>;
+ phy-mode = "rgmii";
+ status = "okay";
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand_1>;
+ status = "okay";
+};
+
+&uart4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart4_1>;
+ status = "okay";
+};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
From df41a18b5ac1401c96dcbce99baa50e339494eba Mon Sep 17 00:00:00 2001
From: Mike Panetta <panetta.mike@gmail.com>
Date: Tue, 30 Jul 2013 20:33:26 -0400
Subject: [PATCH 2/5] ARM: dts: imx6qdl-wandboard: add gpio lines to wandboard
Signed-off-by: Mike Panetta <panetta.mike@gmail.com>
---
arch/arm/boot/dts/imx6qdl-wandboard.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
index 35f5479..a302e95 100644
--- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
@@ -92,6 +92,23 @@
>;
};
};
+
+ gpio {
+ pinctrl_gpio: gpiogrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_DA12__GPIO3_IO12 0x80000000 /* GPIO3_12 EDM pin 255 */
+ MX6QDL_PAD_EIM_DA11__GPIO3_IO11 0x80000000 /* GPIO3_11 EDM pin 256 */
+ MX6QDL_PAD_EIM_DA10__GPIO3_IO10 0x80000000 /* GPIO3_10 EDM pin 257 */
+ MX6QDL_PAD_EIM_D27__GPIO3_IO27 0x80000000 /* GPIO3_27 EDM pin 258 */
+ MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x80000000 /* GPIO3_26 EDM pin 259 */
+ MX6QDL_PAD_EIM_BCLK__GPIO6_IO31 0x80000000 /* GPIO6_31 EDM pin 260 */
+ MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x80000000 /* GPIO3_8 EDM pin 261 */
+ MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24 0x80000000 /* GPIO1_24 EDM pin 262 */
+ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x80000000 /* GPIO4_5 EDM pin 263 */
+ MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 /* GPIO7_8 EDM pin 264 */
+ >;
+ };
+ };
};
&fec {
--
1.8.4.rc3

View File

@@ -0,0 +1,33 @@
From aefbac1e9377311240656ae5061346acb8612e1b Mon Sep 17 00:00:00 2001
From: Michael Panetta <panetta.mike@gmail.com>
Date: Tue, 6 Aug 2013 21:32:50 -0400
Subject: [PATCH 3/5] ARM: dts: imx6qdl-wandboard: Add support for i2c1.
This patch adds support for i2c1 to the wandboard common dtsi file.
Signed-off-by: Michael Panetta <panetta.mike@gmail.com>
---
arch/arm/boot/dts/imx6qdl-wandboard.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
index a302e95..d429c0b 100644
--- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
@@ -58,6 +58,13 @@
status = "okay";
};
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1_1>;
+ status = "okay";
+};
+
&i2c2 {
clock-frequency = <100000>;
pinctrl-names = "default";
--
1.8.4.rc3

View File

@@ -0,0 +1,52 @@
From 1305ac7e9308dcd59c3acc205bc95097cad87ed5 Mon Sep 17 00:00:00 2001
From: Vladimir Ermakov <vooon341@gmail.com>
Date: Fri, 16 Aug 2013 06:52:26 +0400
Subject: [PATCH 5/5] ARM: dts: wandboard: add binding for wand-rfkill driver
Required gpios pincontrol selected in hog. Add binding only.
Disabled non-removable, because after unblocking need to redetect SDIO device.
Signed-off-by: Vladimir Ermakov <vooon341@gmail.com>
---
arch/arm/boot/dts/imx6qdl-wandboard.dtsi | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
index 737805b..c1ef3bd 100644
--- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
@@ -50,6 +50,22 @@
spdif-controller = <&spdif>;
spdif-out;
};
+
+ rfkill {
+ compatible = "wand,imx6qdl-wandboard-rfkill";
+ pinctrl-names = "default";
+ pinctrl-0 = <>;
+
+ bluetooth-on = <&gpio3 13 0>;
+ bluetooth-wake = <&gpio3 14 0>;
+ bluetooth-host-wake = <&gpio3 15 0>;
+
+ wifi-ref-on = <&gpio2 29 0>;
+ wifi-rst-n = <&gpio5 2 0>;
+ wifi-reg-on = <&gpio1 26 0>;
+ wifi-host-wake = <&gpio1 29 0>;
+ wifi-wake = <&gpio1 30 0>;
+ };
};
&audmux {
@@ -175,7 +191,7 @@
&usdhc2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc2_2>;
- non-removable;
+ //non-removable;
status = "okay";
};
--
1.8.4.rc3

View File

@@ -0,0 +1,38 @@
From 3a57291fa4ca7f7647d826f5b47082ef306d839f Mon Sep 17 00:00:00 2001
From: Sean Cross <xobs@kosagi.com>
Date: Thu, 26 Sep 2013 10:51:09 +0800
Subject: [PATCH] ARM: dts: imx6qdl: add pcie device node
Add pcie device node for imx6qdl.
Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
arch/arm/boot/dts/imx6qdl.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -108,6 +108,22 @@
cache-level = <2>;
};
+ pcie: pcie@0x01000000 {
+ compatible = "fsl,imx6q-pcie", "snps,dw-pcie";
+ reg = <0x01ffc000 0x4000>; /* DBI */
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges = <0x00000800 0 0x01f00000 0x01f00000 0 0x00080000 /* configuration space */
+ 0x81000000 0 0 0x01f80000 0 0x00010000 /* downstream I/O */
+ 0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */
+ num-lanes = <1>;
+ interrupts = <0 123 0x04>;
+ clocks = <&clks 189>, <&clks 187>, <&clks 206>, <&clks 144>;
+ clock-names = "pcie_ref_125m", "sata_ref_100m", "lvds_gate", "pcie_axi";
+ status = "disabled";
+ };
+
pmu {
compatible = "arm,cortex-a9-pmu";
interrupts = <0 94 0x04>;

View File

@@ -0,0 +1,38 @@
From: Tim Harvey <tharvey@gateworks.com>
Subject: [PATCH] i2c: imx: retry on NAK
In case of busy i2c try again to get ACK.
Signed-off-by: Tim Harvey <tharvey@gateworks.com>
Tested-by: Luka Perkov <luka@openwrt.org>
---
drivers/i2c/busses/i2c-imx.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -62,6 +62,7 @@
/* Default value */
#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
+#define IMX_I2C_MAX_RETRIES 3 /* number of retries to attempt */
/* IMX I2C registers */
#define IMX_I2C_IADR 0x00 /* i2c slave address */
@@ -198,7 +199,7 @@ static int i2c_imx_acked(struct imx_i2c_
{
if (readb(i2c_imx->base + IMX_I2C_I2SR) & I2SR_RXAK) {
dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__);
- return -EIO; /* No ACK */
+ return -EAGAIN; /* try again */
}
dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__);
@@ -533,6 +534,7 @@ static int __init i2c_imx_probe(struct p
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
+ i2c_imx->adapter.retries = IMX_I2C_MAX_RETRIES;
i2c_imx->base = base;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);

View File

@@ -0,0 +1,29 @@
From fc69065e84165aef5ba7a837d9d2e668bd03b146 Mon Sep 17 00:00:00 2001
From: Vladimir Ermakov <vooon341@gmail.com>
Date: Wed, 10 Jul 2013 03:03:51 +0400
Subject: [PATCH 7/8] i.MX6 Wandboard add CKO1 clock output
stgl5000 uses clock from imx CKO1 pad.
Signed-off-by: Vladimir Ermakov <vooon341@gmail.com>
---
arch/arm/mach-imx/mach-imx6q.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 5536fd8..bf9a30b 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -166,6 +166,9 @@ static void __init imx6q_init_machine(void)
if (of_machine_is_compatible("fsl,imx6q-sabrelite"))
imx6q_sabrelite_init();
+ if (of_machine_is_compatible("wand,imx6q-wandboard"))
+ imx6q_sabrelite_cko1_setup();
+
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
imx_anatop_init();
--
1.7.10.4

View File

@@ -0,0 +1,489 @@
From 744af645bfdb0bfe73fa28df06da48783f85e6a9 Mon Sep 17 00:00:00 2001
From: Shawn Guo <shawn.guo@linaro.org>
Date: Mon, 24 Jun 2013 14:30:44 +0800
Subject: [PATCH 5/5] thermal: add imx thermal driver support
This is based on the initial imx thermal work done by
Rob Lee <rob.lee@linaro.org> (Not sure if the email address is still
valid). Since he is no longer interested in the work and I have
rewritten a significant amount of the code, I just took the authorship
over from him.
It adds the imx thermal support using Temperature Monitor (TEMPMON)
block found on some Freescale i.MX SoCs. The driver uses syscon regmap
interface to access TEMPMON control registers and calibration data, and
supports cpufreq as the cooling device.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
.../devicetree/bindings/thermal/imx-thermal.txt | 17 +
drivers/thermal/Kconfig | 11 +
drivers/thermal/Makefile | 1 +
drivers/thermal/imx_thermal.c | 397 ++++++++++++++++++++
4 files changed, 426 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/imx-thermal.txt
create mode 100644 drivers/thermal/imx_thermal.c
diff --git a/Documentation/devicetree/bindings/thermal/imx-thermal.txt b/Documentation/devicetree/bindings/thermal/imx-thermal.txt
new file mode 100644
index 0000000..541c25e
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/imx-thermal.txt
@@ -0,0 +1,17 @@
+* Temperature Monitor (TEMPMON) on Freescale i.MX SoCs
+
+Required properties:
+- compatible : "fsl,imx6q-thermal"
+- fsl,tempmon : phandle pointer to system controller that contains TEMPMON
+ control registers, e.g. ANATOP on imx6q.
+- fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON
+ calibration data, e.g. OCOTP on imx6q. The details about calibration data
+ can be found in SoC Reference Manual.
+
+Example:
+
+tempmon {
+ compatible = "fsl,imx6q-tempmon";
+ fsl,tempmon = <&anatop>;
+ fsl,tempmon-data = <&ocotp>;
+};
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 5e3c025..e91d78f 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -91,6 +91,17 @@ config THERMAL_EMULATION
because userland can easily disable the thermal policy by simply
flooding this sysfs node with low temperature values.
+config IMX_THERMAL
+ tristate "Temperature sensor driver for Freescale i.MX SoCs"
+ depends on CPU_THERMAL
+ depends on MFD_SYSCON
+ depends on OF
+ help
+ Support for Temperature Monitor (TEMPMON) found on Freescale i.MX SoCs.
+ It supports one critical trip point and one passive trip point. The
+ cpufreq is used as the cooling device to throttle CPUs when the
+ passive trip is crossed.
+
config SPEAR_THERMAL
bool "SPEAr thermal sensor driver"
depends on PLAT_SPEAR
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index c054d41..6910b2d 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
+obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
new file mode 100644
index 0000000..d16c33c
--- /dev/null
+++ b/drivers/thermal/imx_thermal.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/types.h>
+
+#define REG_SET 0x4
+#define REG_CLR 0x8
+#define REG_TOG 0xc
+
+#define MISC0 0x0150
+#define MISC0_REFTOP_SELBIASOFF (1 << 3)
+
+#define TEMPSENSE0 0x0180
+#define TEMPSENSE0_TEMP_CNT_SHIFT 8
+#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
+#define TEMPSENSE0_FINISHED (1 << 2)
+#define TEMPSENSE0_MEASURE_TEMP (1 << 1)
+#define TEMPSENSE0_POWER_DOWN (1 << 0)
+
+#define TEMPSENSE1 0x0190
+#define TEMPSENSE1_MEASURE_FREQ 0xffff
+
+#define OCOTP_ANA1 0x04e0
+
+/* The driver supports 1 passive trip point and 1 critical trip point */
+enum imx_thermal_trip {
+ IMX_TRIP_PASSIVE,
+ IMX_TRIP_CRITICAL,
+ IMX_TRIP_NUM,
+};
+
+/*
+ * It defines the temperature in millicelsius for passive trip point
+ * that will trigger cooling action when crossed.
+ */
+#define IMX_TEMP_PASSIVE 85000
+
+/*
+ * The maximum die temperature on imx parts is 105C, let's give some cushion
+ * for noise and possible temperature rise between measurements.
+ */
+#define IMX_TEMP_CRITICAL 100000
+
+#define IMX_POLLING_DELAY 2000 /* millisecond */
+#define IMX_PASSIVE_DELAY 1000
+
+struct imx_thermal_data {
+ struct thermal_zone_device *tz;
+ struct thermal_cooling_device *cdev;
+ enum thermal_device_mode mode;
+ struct regmap *tempmon;
+ int c1, c2; /* See formula in imx_get_sensor_data() */
+};
+
+static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
+{
+ struct imx_thermal_data *data = tz->devdata;
+ struct regmap *map = data->tempmon;
+ static unsigned long last_temp;
+ unsigned int n_meas;
+ u32 val;
+
+ /*
+ * Every time we measure the temperature, we will power on the
+ * temperature sensor, enable measurements, take a reading,
+ * disable measurements, power off the temperature sensor.
+ */
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+
+ /*
+ * According to the temp sensor designers, it may require up to ~17us
+ * to complete a measurement.
+ */
+ usleep_range(20, 50);
+
+ regmap_read(map, TEMPSENSE0, &val);
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
+
+ if ((val & TEMPSENSE0_FINISHED) == 0) {
+ dev_dbg(&tz->device, "temp measurement never finished\n");
+ return -EAGAIN;
+ }
+
+ n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT;
+
+ /* See imx_get_sensor_data() for formula derivation */
+ *temp = data->c2 + data->c1 * n_meas;
+
+ if (*temp != last_temp) {
+ dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
+ last_temp = *temp;
+ }
+
+ return 0;
+}
+
+static int imx_get_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode *mode)
+{
+ struct imx_thermal_data *data = tz->devdata;
+
+ *mode = data->mode;
+
+ return 0;
+}
+
+static int imx_set_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode mode)
+{
+ struct imx_thermal_data *data = tz->devdata;
+
+ if (mode == THERMAL_DEVICE_ENABLED) {
+ tz->polling_delay = IMX_POLLING_DELAY;
+ tz->passive_delay = IMX_PASSIVE_DELAY;
+ } else {
+ tz->polling_delay = 0;
+ tz->passive_delay = 0;
+ }
+
+ data->mode = mode;
+ thermal_zone_device_update(tz);
+
+ return 0;
+}
+
+static int imx_get_trip_type(struct thermal_zone_device *tz, int trip,
+ enum thermal_trip_type *type)
+{
+ *type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
+ THERMAL_TRIP_CRITICAL;
+ return 0;
+}
+
+static int imx_get_crit_temp(struct thermal_zone_device *tz,
+ unsigned long *temp)
+{
+ *temp = IMX_TEMP_CRITICAL;
+ return 0;
+}
+
+static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
+ unsigned long *temp)
+{
+ *temp = (trip == IMX_TRIP_PASSIVE) ? IMX_TEMP_PASSIVE :
+ IMX_TEMP_CRITICAL;
+ return 0;
+}
+
+static int imx_bind(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int ret;
+
+ ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev,
+ THERMAL_NO_LIMIT,
+ THERMAL_NO_LIMIT);
+ if (ret) {
+ dev_err(&tz->device,
+ "binding zone %s with cdev %s failed:%d\n",
+ tz->type, cdev->type, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_unbind(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int ret;
+
+ ret = thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev);
+ if (ret) {
+ dev_err(&tz->device,
+ "unbinding zone %s with cdev %s failed:%d\n",
+ tz->type, cdev->type, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops imx_tz_ops = {
+ .bind = imx_bind,
+ .unbind = imx_unbind,
+ .get_temp = imx_get_temp,
+ .get_mode = imx_get_mode,
+ .set_mode = imx_set_mode,
+ .get_trip_type = imx_get_trip_type,
+ .get_trip_temp = imx_get_trip_temp,
+ .get_crit_temp = imx_get_crit_temp,
+};
+
+static int imx_get_sensor_data(struct platform_device *pdev)
+{
+ struct imx_thermal_data *data = platform_get_drvdata(pdev);
+ struct regmap *map;
+ int t1, t2, n1, n2;
+ int ret;
+ u32 val;
+
+ map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "fsl,tempmon-data");
+ if (IS_ERR(map)) {
+ ret = PTR_ERR(map);
+ dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(map, OCOTP_ANA1, &val);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret);
+ return ret;
+ }
+
+ if (val == 0 || val == ~0) {
+ dev_err(&pdev->dev, "invalid sensor calibration data\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Sensor data layout:
+ * [31:20] - sensor value @ 25C
+ * [19:8] - sensor value of hot
+ * [7:0] - hot temperature value
+ */
+ n1 = val >> 20;
+ n2 = (val & 0xfff00) >> 8;
+ t2 = val & 0xff;
+ t1 = 25; /* t1 always 25C */
+
+ /*
+ * Derived from linear interpolation,
+ * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2)
+ * We want to reduce this down to the minimum computation necessary
+ * for each temperature read. Also, we want Tmeas in millicelsius
+ * and we don't want to lose precision from integer division. So...
+ * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2)
+ * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2)
+ * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2)
+ * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2)
+ * Let constant c2 = (1000 * T2) - (c1 * N2)
+ * milli_Tmeas = c2 + (c1 * Nmeas)
+ */
+ data->c1 = 1000 * (t1 - t2) / (n1 - n2);
+ data->c2 = 1000 * t2 - data->c1 * n2;
+
+ return 0;
+}
+
+static int imx_thermal_probe(struct platform_device *pdev)
+{
+ struct imx_thermal_data *data;
+ struct cpumask clip_cpus;
+ struct regmap *map;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon");
+ if (IS_ERR(map)) {
+ ret = PTR_ERR(map);
+ dev_err(&pdev->dev, "failed to get tempmon regmap: %d\n", ret);
+ return ret;
+ }
+ data->tempmon = map;
+
+ platform_set_drvdata(pdev, data);
+
+ ret = imx_get_sensor_data(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get sensor data\n");
+ return ret;
+ }
+
+ /* Make sure sensor is in known good state for measurements */
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
+ regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
+ regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF);
+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
+
+ cpumask_set_cpu(0, &clip_cpus);
+ data->cdev = cpufreq_cooling_register(&clip_cpus);
+ if (IS_ERR(data->cdev)) {
+ ret = PTR_ERR(data->cdev);
+ dev_err(&pdev->dev,
+ "failed to register cpufreq cooling device: %d\n", ret);
+ return ret;
+ }
+
+ data->tz = thermal_zone_device_register("imx_thermal_zone",
+ IMX_TRIP_NUM, 0, data,
+ &imx_tz_ops, NULL,
+ IMX_PASSIVE_DELAY,
+ IMX_POLLING_DELAY);
+ if (IS_ERR(data->tz)) {
+ ret = PTR_ERR(data->tz);
+ dev_err(&pdev->dev,
+ "failed to register thermal zone device %d\n", ret);
+ cpufreq_cooling_unregister(data->cdev);
+ return ret;
+ }
+
+ data->mode = THERMAL_DEVICE_ENABLED;
+
+ return 0;
+}
+
+static int imx_thermal_remove(struct platform_device *pdev)
+{
+ struct imx_thermal_data *data = platform_get_drvdata(pdev);
+
+ thermal_zone_device_unregister(data->tz);
+ cpufreq_cooling_unregister(data->cdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int imx_thermal_suspend(struct device *dev)
+{
+ struct imx_thermal_data *data = dev_get_drvdata(dev);
+ struct regmap *map = data->tempmon;
+ u32 val;
+
+ regmap_read(map, TEMPSENSE0, &val);
+ if ((val & TEMPSENSE0_POWER_DOWN) == 0) {
+ /*
+ * If a measurement is taking place, wait for a long enough
+ * time for it to finish, and then check again. If it still
+ * does not finish, something must go wrong.
+ */
+ udelay(50);
+ regmap_read(map, TEMPSENSE0, &val);
+ if ((val & TEMPSENSE0_POWER_DOWN) == 0)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int imx_thermal_resume(struct device *dev)
+{
+ /* Nothing to do for now */
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
+ imx_thermal_suspend, imx_thermal_resume);
+
+static const struct of_device_id of_imx_thermal_match[] = {
+ { .compatible = "fsl,imx6q-tempmon", },
+ { /* end */ }
+};
+
+static struct platform_driver imx_thermal = {
+ .driver = {
+ .name = "imx_thermal",
+ .owner = THIS_MODULE,
+ .pm = &imx_thermal_pm_ops,
+ .of_match_table = of_imx_thermal_match,
+ },
+ .probe = imx_thermal_probe,
+ .remove = imx_thermal_remove,
+};
+module_platform_driver(imx_thermal);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Thermal driver for Freescale i.MX SoCs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-thermal");
--
1.7.10.4

View File

@@ -0,0 +1,341 @@
From adf0f7b7d7c0083dd936fe46423b89e974f8df12 Mon Sep 17 00:00:00 2001
From: Vladimir Ermakov <vooon341@gmail.com>
Date: Wed, 10 Jul 2013 03:06:54 +0400
Subject: [PATCH] ARM i.MX6 Wandboard add wifi+bt rfkill driver
BRCM WiFi module requires initialization for control gpio;
Additional provides rfkill funcs.
v2: fix wrong probe func in driver struct
v3: add imx6qdl compatible
Signed-off-by: Vladimir Ermakov <vooon341@gmail.com>
---
arch/arm/mach-imx/devices/Kconfig | 6 +
arch/arm/mach-imx/devices/Makefile | 1 +
arch/arm/mach-imx/devices/wand-rfkill.c | 290 ++++++++++++++++++++++++++++++++
3 files changed, 297 insertions(+)
create mode 100644 arch/arm/mach-imx/devices/wand-rfkill.c
diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig
index 68c74fb..a0adf75 100644
--- a/arch/arm/mach-imx/devices/Kconfig
+++ b/arch/arm/mach-imx/devices/Kconfig
@@ -85,3 +85,9 @@ config IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX
config IMX_HAVE_PLATFORM_SPI_IMX
bool
+
+config WAND_RFKILL
+ tristate "Wandboard RF Kill support"
+ depends on SOC_IMX6Q
+ default m
+ select RFKILL
diff --git a/arch/arm/mach-imx/devices/Makefile b/arch/arm/mach-imx/devices/Makefile
index 67416fb..b2aded5 100644
--- a/arch/arm/mach-imx/devices/Makefile
+++ b/arch/arm/mach-imx/devices/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_EMMA) += platform-mx2-emma.o
+obj-$(CONFIG_WAND_RFKILL) += wand-rfkill.o
diff --git a/arch/arm/mach-imx/devices/wand-rfkill.c b/arch/arm/mach-imx/devices/wand-rfkill.c
new file mode 100644
index 0000000..da7ef9f
--- /dev/null
+++ b/arch/arm/mach-imx/devices/wand-rfkill.c
@@ -0,0 +1,290 @@
+/*
+ * arch/arm/mach-imx/devices/wand-rfkill.c
+ *
+ * Copyright (C) 2013 Vladimir Ermakov <vooon341@gmail.com>
+ *
+ * based on net/rfkill/rfkill-gpio.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+
+struct wand_rfkill_data {
+ struct rfkill *rfkill_dev;
+ int shutdown_gpio;
+ const char *shutdown_name;
+};
+
+static int wand_rfkill_set_block(void *data, bool blocked)
+{
+ struct wand_rfkill_data *rfkill = data;
+
+ pr_debug("wandboard-rfkill: set block %d\n", blocked);
+
+ if (blocked) {
+ if (gpio_is_valid(rfkill->shutdown_gpio))
+ gpio_direction_output(rfkill->shutdown_gpio, 0);
+ } else {
+ if (gpio_is_valid(rfkill->shutdown_gpio))
+ gpio_direction_output(rfkill->shutdown_gpio, 1);
+ }
+
+ return 0;
+}
+
+static const struct rfkill_ops wand_rfkill_ops = {
+ .set_block = wand_rfkill_set_block,
+};
+
+static int wand_rfkill_wifi_probe(struct device *dev,
+ struct device_node *np,
+ struct wand_rfkill_data *rfkill)
+{
+ int ret;
+ int wl_ref_on, wl_rst_n, wl_reg_on, wl_wake, wl_host_wake;
+
+ wl_ref_on = of_get_named_gpio(np, "wifi-ref-on", 0);
+ wl_rst_n = of_get_named_gpio(np, "wifi-rst-n", 0);
+ wl_reg_on = of_get_named_gpio(np, "wifi-reg-on", 0);
+ wl_wake = of_get_named_gpio(np, "wifi-wake", 0);
+ wl_host_wake = of_get_named_gpio(np, "wifi-host-wake", 0);
+
+ if (!gpio_is_valid(wl_rst_n) || !gpio_is_valid(wl_ref_on) ||
+ !gpio_is_valid(wl_reg_on) || !gpio_is_valid(wl_wake) ||
+ !gpio_is_valid(wl_host_wake)) {
+
+ dev_err(dev, "incorrect wifi gpios (%d %d %d %d %d)\n",
+ wl_rst_n, wl_ref_on, wl_reg_on, wl_wake, wl_host_wake);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "initialize wifi chip\n");
+
+ gpio_request(wl_rst_n, "wl_rst_n");
+ gpio_direction_output(wl_rst_n, 0);
+ msleep(11);
+ gpio_set_value(wl_rst_n, 1);
+
+ gpio_request(wl_ref_on, "wl_ref_on");
+ gpio_direction_output(wl_ref_on, 1);
+
+ gpio_request(wl_reg_on, "wl_reg_on");
+ gpio_direction_output(wl_reg_on, 1);
+
+ gpio_request(wl_wake, "wl_wake");
+ gpio_direction_output(wl_wake, 1);
+
+ gpio_request(wl_host_wake, "wl_host_wake");
+ gpio_direction_input(wl_host_wake);
+
+ rfkill->shutdown_name = "wifi_shutdown";
+ rfkill->shutdown_gpio = wl_wake;
+
+ rfkill->rfkill_dev = rfkill_alloc("wifi-rfkill", dev, RFKILL_TYPE_WLAN,
+ &wand_rfkill_ops, rfkill);
+ if (!rfkill->rfkill_dev) {
+ ret = -ENOMEM;
+ goto wifi_fail_free_gpio;
+ }
+
+ ret = rfkill_register(rfkill->rfkill_dev);
+ if (ret < 0)
+ goto wifi_fail_unregister;
+
+ dev_info(dev, "wifi-rfkill registered.\n");
+
+ return 0;
+
+wifi_fail_unregister:
+ rfkill_destroy(rfkill->rfkill_dev);
+wifi_fail_free_gpio:
+ if (gpio_is_valid(wl_rst_n)) gpio_free(wl_rst_n);
+ if (gpio_is_valid(wl_ref_on)) gpio_free(wl_ref_on);
+ if (gpio_is_valid(wl_reg_on)) gpio_free(wl_reg_on);
+ if (gpio_is_valid(wl_wake)) gpio_free(wl_wake);
+ if (gpio_is_valid(wl_host_wake)) gpio_free(wl_host_wake);
+
+ return ret;
+}
+
+static int wand_rfkill_bt_probe(struct device *dev,
+ struct device_node *np,
+ struct wand_rfkill_data *rfkill)
+{
+ int ret;
+ int bt_on, bt_wake, bt_host_wake;
+
+ bt_on = of_get_named_gpio(np, "bluetooth-on", 0);
+ bt_wake = of_get_named_gpio(np, "bluetooth-wake", 0);
+ bt_host_wake = of_get_named_gpio(np, "bluetooth-host-wake", 0);
+
+ if (!gpio_is_valid(bt_on) || !gpio_is_valid(bt_wake) ||
+ !gpio_is_valid(bt_host_wake)) {
+
+ dev_err(dev, "incorrect bt gpios (%d %d %d)\n",
+ bt_on, bt_wake, bt_host_wake);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "initialize bluetooth chip\n");
+
+ gpio_request(bt_on, "bt_on");
+ gpio_direction_output(bt_on, 0);
+ msleep(11);
+ gpio_set_value(bt_on, 1);
+
+ gpio_request(bt_wake, "bt_wake");
+ gpio_direction_output(bt_wake, 1);
+
+ gpio_request(bt_host_wake, "bt_host_wake");
+ gpio_direction_input(bt_host_wake);
+
+ rfkill->shutdown_name = "bluetooth_shutdown";
+ rfkill->shutdown_gpio = bt_wake;
+
+ rfkill->rfkill_dev = rfkill_alloc("bluetooth-rfkill", dev, RFKILL_TYPE_BLUETOOTH,
+ &wand_rfkill_ops, rfkill);
+ if (!rfkill->rfkill_dev) {
+ ret = -ENOMEM;
+ goto bt_fail_free_gpio;
+ }
+
+ ret = rfkill_register(rfkill->rfkill_dev);
+ if (ret < 0)
+ goto bt_fail_unregister;
+
+ dev_info(dev, "bluetooth-rfkill registered.\n");
+
+ return 0;
+
+bt_fail_unregister:
+ rfkill_destroy(rfkill->rfkill_dev);
+bt_fail_free_gpio:
+ if (gpio_is_valid(bt_on)) gpio_free(bt_on);
+ if (gpio_is_valid(bt_wake)) gpio_free(bt_wake);
+ if (gpio_is_valid(bt_host_wake)) gpio_free(bt_host_wake);
+
+ return ret;
+}
+
+static int wand_rfkill_probe(struct platform_device *pdev)
+{
+ struct wand_rfkill_data *rfkill;
+ struct pinctrl *pinctrl;
+ int ret;
+
+ dev_info(&pdev->dev, "Wandboard rfkill initialization\n");
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "no device tree node\n");
+ return -ENODEV;
+ }
+
+ rfkill = kzalloc(sizeof(*rfkill) * 2, GFP_KERNEL);
+ if (!rfkill)
+ return -ENOMEM;
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ int ret = PTR_ERR(pinctrl);
+ dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
+ return ret;
+ }
+
+ /* setup WiFi */
+ ret = wand_rfkill_wifi_probe(&pdev->dev, pdev->dev.of_node, &rfkill[0]);
+ if (ret < 0)
+ goto fail_free_rfkill;
+
+ /* setup bluetooth */
+ ret = wand_rfkill_bt_probe(&pdev->dev, pdev->dev.of_node, &rfkill[1]);
+ if (ret < 0)
+ goto fail_unregister_wifi;
+
+ platform_set_drvdata(pdev, rfkill);
+
+ return 0;
+
+fail_unregister_wifi:
+ if (rfkill[1].rfkill_dev) {
+ rfkill_unregister(rfkill[1].rfkill_dev);
+ rfkill_destroy(rfkill[1].rfkill_dev);
+ }
+
+ /* TODO free gpio */
+
+fail_free_rfkill:
+ kfree(rfkill);
+
+ return ret;
+}
+
+static int wand_rfkill_remove(struct platform_device *pdev)
+{
+ struct wand_rfkill_data *rfkill = platform_get_drvdata(pdev);
+
+ dev_info(&pdev->dev, "Module unloading\n");
+
+ if (!rfkill)
+ return 0;
+
+ /* WiFi */
+ if (gpio_is_valid(rfkill[0].shutdown_gpio))
+ gpio_free(rfkill[0].shutdown_gpio);
+
+ rfkill_unregister(rfkill[0].rfkill_dev);
+ rfkill_destroy(rfkill[0].rfkill_dev);
+
+ /* Bt */
+ if (gpio_is_valid(rfkill[1].shutdown_gpio))
+ gpio_free(rfkill[1].shutdown_gpio);
+
+ rfkill_unregister(rfkill[1].rfkill_dev);
+ rfkill_destroy(rfkill[1].rfkill_dev);
+
+ kfree(rfkill);
+
+ return 0;
+}
+
+static struct of_device_id wand_rfkill_match[] = {
+ { .compatible = "wand,imx6q-wandboard-rfkill", },
+ { .compatible = "wand,imx6dl-wandboard-rfkill", },
+ { .compatible = "wand,imx6qdl-wandboard-rfkill", },
+ {}
+};
+
+static struct platform_driver wand_rfkill_driver = {
+ .driver = {
+ .name = "wandboard-rfkill",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wand_rfkill_match),
+ },
+ .probe = wand_rfkill_probe,
+ .remove = wand_rfkill_remove
+};
+
+module_platform_driver(wand_rfkill_driver);
+
+MODULE_AUTHOR("Vladimir Ermakov <vooon341@gmail.com>");
+MODULE_DESCRIPTION("Wandboard rfkill driver");
+MODULE_LICENSE("GPL v2");
--
1.8.4.rc3

View File

@@ -0,0 +1,85 @@
From 8e890a259208dbe3aba6f46f7c3a213269d8f123 Mon Sep 17 00:00:00 2001
From: Allen Ibara <allen@zee.aero>
Date: Tue, 4 Dec 2012 20:44:26 -0800
Subject: [PATCH 2/5] Add IMX6Q AHCI support
Adds ahci_platform bits to make AHCI work on sabrelite IMX6Q board.
Signed-off-by: Allen Ibara <allen@zee.aero>
Signed-off-by: Robert Nelson <robertcnelson@gmail.com>
---
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 7a8a284..d324cdf 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -23,6 +23,9 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include "ahci.h"
static void ahci_host_stop(struct ata_host *host);
@@ -30,6 +33,7 @@ static void ahci_host_stop(struct ata_host *host);
enum ahci_type {
AHCI, /* standard platform ahci */
IMX53_AHCI, /* ahci on i.mx53 */
+ IMX6Q_AHCI, /* ahci on i.mx6q */
STRICT_AHCI, /* delayed DMA engine start */
};
@@ -41,6 +45,9 @@ static struct platform_device_id ahci_devtype[] = {
.name = "imx53-ahci",
.driver_data = IMX53_AHCI,
}, {
+ .name = "imx6q-ahci",
+ .driver_data = IMX53_AHCI,
+ }, {
.name = "strict-ahci",
.driver_data = STRICT_AHCI,
}, {
@@ -86,12 +93,24 @@ static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT("ahci_platform"),
};
+static const struct of_device_id ahci_of_match[] = {
+ { .compatible = "calxeda,hb-ahci", .data = &ahci_devtype[AHCI],},
+ { .compatible = "fsl,imx6q-ahci", .data = &ahci_devtype[IMX6Q_AHCI],},
+ { .compatible = "snps,spear-ahci", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ahci_of_match);
+
static int ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_platform_data *pdata = dev_get_platdata(dev);
+ const struct of_device_id *of_id =
+ of_match_device(ahci_of_match, &pdev->dev);
+ const struct platform_device_id *id_entry = of_id->data;
const struct platform_device_id *id = platform_get_device_id(pdev);
- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
+ struct ata_port_info pi = ahci_port_info[id ? id->driver_data : \
+ id_entry->driver_data];
const struct ata_port_info *ppi[] = { &pi, NULL };
struct ahci_host_priv *hpriv;
struct ata_host *host;
@@ -325,12 +344,6 @@ disable_unprepare_clk:
static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
-static const struct of_device_id ahci_of_match[] = {
- { .compatible = "snps,spear-ahci", },
- {},
-};
-MODULE_DEVICE_TABLE(of, ahci_of_match);
-
static struct platform_driver ahci_driver = {
.probe = ahci_probe,
.remove = ata_platform_remove_one,
--
1.7.10.4

View File

@@ -0,0 +1,25 @@
From 41cc1967181a833c3c5af30682ea85dd01c28ff4 Mon Sep 17 00:00:00 2001
From: Robert Nelson <robertcnelson@gmail.com>
Date: Tue, 22 Jan 2013 22:21:03 -0600
Subject: [PATCH 3/5] imx: Add IMX53 AHCI support
Adds ahci_platform bits to make AHCI work on mx53 qsb board.
Signed-off-by: Robert Nelson <robertcnelson@gmail.com>
---
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index d324cdf..b01eeca 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -95,6 +95,7 @@ static struct scsi_host_template ahci_platform_sht = {
static const struct of_device_id ahci_of_match[] = {
{ .compatible = "calxeda,hb-ahci", .data = &ahci_devtype[AHCI],},
+ { .compatible = "fsl,imx53-ahci", .data = &ahci_devtype[IMX53_AHCI],},
{ .compatible = "fsl,imx6q-ahci", .data = &ahci_devtype[IMX6Q_AHCI],},
{ .compatible = "snps,spear-ahci", },
{},
--
1.7.10.4

View File

@@ -0,0 +1,33 @@
From 765561c8c72a46c2177b20d730e061ab2ff8f970 Mon Sep 17 00:00:00 2001
From: Paolo Pisati <paolo.pisati@canonical.com>
Date: Thu, 31 Jan 2013 18:33:46 +0100
Subject: [PATCH 4/5] SAUCE: imx6: enable sata clk if SATA_AHCI_PLATFORM
Clock modifications in 24d340ac "ARM i.MX6: Fix ethernet PLL clocks" broke the
SATA clk, fix it.
More info: http://www.spinics.net/lists/arm-kernel/msg221503.html
Original-code-from: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
---
arch/arm/mach-imx/clk-imx6q.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 4e3148c..38d707a 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -568,6 +568,9 @@ int __init mx6q_clocks_init(void)
clk_prepare_enable(clk[usbphy2_gate]);
}
+ if (IS_ENABLED(CONFIG_SATA_AHCI_PLATFORM))
+ clk_prepare_enable(clk[sata_ref_100m]);
+
/* Set initial power mode */
imx6q_set_lpm(WAIT_CLOCKED);
--
1.7.10.4

View File

@@ -0,0 +1,150 @@
From 371863a788db77e6092d69df17d8884cb0d94270 Mon Sep 17 00:00:00 2001
From: Richard Zhu <r65037@freescale.com>
Date: Wed, 24 Jul 2013 06:15:28 +0000
Subject: [PATCH 1/2] ARM: imx6q: update the sata bits definitions of gpr13
Replace the SATA_PHY_# by the more readable definitons.
tj: Being routed through libata branch to enable implementation of
ahci_imx.
Signed-off-by: Richard Zhu <r65037@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
---
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 121 +++++++++++++++++++--------
1 file changed, 84 insertions(+), 37 deletions(-)
diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
index b1521e8..b6bdcd6 100644
--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
@@ -279,41 +279,88 @@
#define IMX6Q_GPR13_CAN2_STOP_REQ BIT(29)
#define IMX6Q_GPR13_CAN1_STOP_REQ BIT(28)
#define IMX6Q_GPR13_ENET_STOP_REQ BIT(27)
-#define IMX6Q_GPR13_SATA_PHY_8_MASK (0x7 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_0_5_DB (0x0 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_1_0_DB (0x1 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_1_5_DB (0x2 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_2_0_DB (0x3 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_2_5_DB (0x4 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_3_0_DB (0x5 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_3_5_DB (0x6 << 24)
-#define IMX6Q_GPR13_SATA_PHY_8_4_0_DB (0x7 << 24)
-#define IMX6Q_GPR13_SATA_PHY_7_MASK (0x1f << 19)
-#define IMX6Q_GPR13_SATA_PHY_7_SATA1I (0x10 << 19)
-#define IMX6Q_GPR13_SATA_PHY_7_SATA1M (0x10 << 19)
-#define IMX6Q_GPR13_SATA_PHY_7_SATA1X (0x1a << 19)
-#define IMX6Q_GPR13_SATA_PHY_7_SATA2I (0x12 << 19)
-#define IMX6Q_GPR13_SATA_PHY_7_SATA2M (0x12 << 19)
-#define IMX6Q_GPR13_SATA_PHY_7_SATA2X (0x1a << 19)
-#define IMX6Q_GPR13_SATA_PHY_6_MASK (0x7 << 16)
-#define IMX6Q_GPR13_SATA_SPEED_MASK BIT(15)
-#define IMX6Q_GPR13_SATA_SPEED_1P5G 0x0
-#define IMX6Q_GPR13_SATA_SPEED_3P0G BIT(15)
-#define IMX6Q_GPR13_SATA_PHY_5 BIT(14)
-#define IMX6Q_GPR13_SATA_PHY_4_MASK (0x7 << 11)
-#define IMX6Q_GPR13_SATA_PHY_4_16_16 (0x0 << 11)
-#define IMX6Q_GPR13_SATA_PHY_4_14_16 (0x1 << 11)
-#define IMX6Q_GPR13_SATA_PHY_4_12_16 (0x2 << 11)
-#define IMX6Q_GPR13_SATA_PHY_4_10_16 (0x3 << 11)
-#define IMX6Q_GPR13_SATA_PHY_4_9_16 (0x4 << 11)
-#define IMX6Q_GPR13_SATA_PHY_4_8_16 (0x5 << 11)
-#define IMX6Q_GPR13_SATA_PHY_3_MASK (0xf << 7)
-#define IMX6Q_GPR13_SATA_PHY_3_OFF 0x7
-#define IMX6Q_GPR13_SATA_PHY_2_MASK (0x1f << 2)
-#define IMX6Q_GPR13_SATA_PHY_2_OFF 0x2
-#define IMX6Q_GPR13_SATA_PHY_1_MASK (0x3 << 0)
-#define IMX6Q_GPR13_SATA_PHY_1_FAST (0x0 << 0)
-#define IMX6Q_GPR13_SATA_PHY_1_MED (0x1 << 0)
-#define IMX6Q_GPR13_SATA_PHY_1_SLOW (0x2 << 0)
-
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK (0x7 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_0_5_DB (0x0 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_1_0_DB (0x1 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_1_5_DB (0x2 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_2_0_DB (0x3 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_2_5_DB (0x4 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB (0x5 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_3_5_DB (0x6 << 24)
+#define IMX6Q_GPR13_SATA_RX_EQ_VAL_4_0_DB (0x7 << 24)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK (0x1f << 19)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA1I (0x10 << 19)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA1M (0x10 << 19)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA1X (0x1a << 19)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2I (0x12 << 19)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M (0x12 << 19)
+#define IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2X (0x1a << 19)
+#define IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK (0x7 << 16)
+#define IMX6Q_GPR13_SATA_RX_DPLL_MODE_1P_1F (0x0 << 16)
+#define IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_2F (0x1 << 16)
+#define IMX6Q_GPR13_SATA_RX_DPLL_MODE_1P_4F (0x2 << 16)
+#define IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F (0x3 << 16)
+#define IMX6Q_GPR13_SATA_SPD_MODE_MASK BIT(15)
+#define IMX6Q_GPR13_SATA_SPD_MODE_1P5G 0x0
+#define IMX6Q_GPR13_SATA_SPD_MODE_3P0G BIT(15)
+#define IMX6Q_GPR13_SATA_MPLL_SS_EN BIT(14)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_MASK (0x7 << 11)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_16_16 (0x0 << 11)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_14_16 (0x1 << 11)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_12_16 (0x2 << 11)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_10_16 (0x3 << 11)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_9_16 (0x4 << 11)
+#define IMX6Q_GPR13_SATA_TX_ATTEN_8_16 (0x5 << 11)
+#define IMX6Q_GPR13_SATA_TX_BOOST_MASK (0xf << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_0_00_DB (0x0 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_0_37_DB (0x1 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_0_74_DB (0x2 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB (0x3 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB (0x4 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB (0x5 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB (0x6 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB (0x7 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB (0x8 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB (0x9 << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB (0xa << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB (0xb << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB (0xc << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB (0xd << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB (0xe << 7)
+#define IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB (0xf << 7)
+#define IMX6Q_GPR13_SATA_TX_LVL_MASK (0x1f << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_937_V (0x00 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_947_V (0x01 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_957_V (0x02 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_966_V (0x03 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_976_V (0x04 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_986_V (0x05 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_0_996_V (0x06 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_005_V (0x07 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_015_V (0x08 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_025_V (0x09 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_035_V (0x0a << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_045_V (0x0b << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_054_V (0x0c << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_064_V (0x0d << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_074_V (0x0e << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_084_V (0x0f << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_094_V (0x10 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_104_V (0x11 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_113_V (0x12 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_123_V (0x13 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_133_V (0x14 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_143_V (0x15 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_152_V (0x16 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_162_V (0x17 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_172_V (0x18 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_182_V (0x19 << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_191_V (0x1a << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_201_V (0x1b << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_211_V (0x1c << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_221_V (0x1d << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_230_V (0x1e << 2)
+#define IMX6Q_GPR13_SATA_TX_LVL_1_240_V (0x1f << 2)
+#define IMX6Q_GPR13_SATA_MPLL_CLK_EN BIT(1)
+#define IMX6Q_GPR13_SATA_TX_EDGE_RATE BIT(0)
#endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */
--
1.7.10.4

View File

@@ -0,0 +1,316 @@
From 093f4fdd74f29031d79be747c65b95fb16601a92 Mon Sep 17 00:00:00 2001
From: Richard Zhu <r65037@freescale.com>
Date: Wed, 24 Jul 2013 06:15:29 +0000
Subject: [PATCH 2/2] ahci_imx: add ahci sata support on imx platforms
imx6q contains one Synopsys AHCI SATA controller, But it can't share
ahci_platform driver with other controllers because there are some
misalignments of the generic AHCI controller - the bits definitions of
the HBA registers, the Vendor Specific registers, the AHCI PHY clock
and the AHCI signals adjustment window(GPR13 register).
- CAP_SSS(bit20) of the HOST_CAP is writable, default value is '0',
should be configured to be '1'
- bit0 (only one AHCI SATA port on imx6q) of the HOST_PORTS_IMPL
should be set to be '1'.(default 0)
- One Vendor Specific register HOST_TIMER1MS(offset:0xe0) should be
configured regarding to the frequency of AHB bus clock.
- Configurations of the AHCI PHY clock, and the signal parameters of
the GPR13
Setup its own ahci sata driver, contained the imx6q specific
initialized codes, re-use the generic ahci_platform driver, and keep
the generic ahci_platform driver clean as much as possible.
tj: patch description reformatted
Signed-off-by: Richard Zhu <r65037@freescale.com>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
---
drivers/ata/Kconfig | 9 ++
drivers/ata/Makefile | 1 +
drivers/ata/ahci_imx.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 246 insertions(+)
create mode 100644 drivers/ata/ahci_imx.c
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 80dc988..cbf7a16 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -97,6 +97,15 @@ config SATA_AHCI_PLATFORM
If unsure, say N.
+config AHCI_IMX
+ tristate "Freescale i.MX AHCI SATA support"
+ depends on SATA_AHCI_PLATFORM
+ help
+ This option enables support for the Freescale i.MX SoC's
+ onboard AHCI SATA.
+
+ If unsure, say N.
+
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index c04d0fd..46518c6 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
+obj-$(CONFIG_AHCI_IMX) += ahci_imx.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c
new file mode 100644
index 0000000..58debb0
--- /dev/null
+++ b/drivers/ata/ahci_imx.c
@@ -0,0 +1,236 @@
+/*
+ * Freescale IMX AHCI SATA platform driver
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/ahci_platform.h>
+#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include "ahci.h"
+
+enum {
+ HOST_TIMER1MS = 0xe0, /* Timer 1-ms */
+};
+
+struct imx_ahci_priv {
+ struct platform_device *ahci_pdev;
+ struct clk *sata_ref_clk;
+ struct clk *ahb_clk;
+ struct regmap *gpr;
+};
+
+static int imx6q_sata_init(struct device *dev, void __iomem *mmio)
+{
+ int ret = 0;
+ unsigned int reg_val;
+ struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
+
+ imxpriv->gpr =
+ syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+ if (IS_ERR(imxpriv->gpr)) {
+ dev_err(dev, "failed to find fsl,imx6q-iomux-gpr regmap\n");
+ return PTR_ERR(imxpriv->gpr);
+ }
+
+ ret = clk_prepare_enable(imxpriv->sata_ref_clk);
+ if (ret < 0) {
+ dev_err(dev, "prepare-enable sata_ref clock err:%d\n", ret);
+ return ret;
+ }
+
+ /*
+ * set PHY Paremeters, two steps to configure the GPR13,
+ * one write for rest of parameters, mask of first write
+ * is 0x07fffffd, and the other one write for setting
+ * the mpll_clk_en.
+ */
+ regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK
+ | IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK
+ | IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK
+ | IMX6Q_GPR13_SATA_SPD_MODE_MASK
+ | IMX6Q_GPR13_SATA_MPLL_SS_EN
+ | IMX6Q_GPR13_SATA_TX_ATTEN_MASK
+ | IMX6Q_GPR13_SATA_TX_BOOST_MASK
+ | IMX6Q_GPR13_SATA_TX_LVL_MASK
+ | IMX6Q_GPR13_SATA_TX_EDGE_RATE
+ , IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB
+ | IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M
+ | IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F
+ | IMX6Q_GPR13_SATA_SPD_MODE_3P0G
+ | IMX6Q_GPR13_SATA_MPLL_SS_EN
+ | IMX6Q_GPR13_SATA_TX_ATTEN_9_16
+ | IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB
+ | IMX6Q_GPR13_SATA_TX_LVL_1_025_V);
+ regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN,
+ IMX6Q_GPR13_SATA_MPLL_CLK_EN);
+ usleep_range(100, 200);
+
+ /*
+ * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL,
+ * and IP vendor specific register HOST_TIMER1MS.
+ * Configure CAP_SSS (support stagered spin up).
+ * Implement the port0.
+ * Get the ahb clock rate, and configure the TIMER1MS register.
+ */
+ reg_val = readl(mmio + HOST_CAP);
+ if (!(reg_val & HOST_CAP_SSS)) {
+ reg_val |= HOST_CAP_SSS;
+ writel(reg_val, mmio + HOST_CAP);
+ }
+ reg_val = readl(mmio + HOST_PORTS_IMPL);
+ if (!(reg_val & 0x1)) {
+ reg_val |= 0x1;
+ writel(reg_val, mmio + HOST_PORTS_IMPL);
+ }
+
+ reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000;
+ writel(reg_val, mmio + HOST_TIMER1MS);
+
+ return 0;
+}
+
+static void imx6q_sata_exit(struct device *dev)
+{
+ struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
+
+ regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN,
+ !IMX6Q_GPR13_SATA_MPLL_CLK_EN);
+ clk_disable_unprepare(imxpriv->sata_ref_clk);
+}
+
+static struct ahci_platform_data imx6q_sata_pdata = {
+ .init = imx6q_sata_init,
+ .exit = imx6q_sata_exit,
+};
+
+static const struct of_device_id imx_ahci_of_match[] = {
+ { .compatible = "fsl,imx6q-ahci", .data = &imx6q_sata_pdata},
+ {},
+};
+MODULE_DEVICE_TABLE(of, imx_ahci_of_match);
+
+static int imx_ahci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *mem, *irq, res[2];
+ const struct of_device_id *of_id;
+ const struct ahci_platform_data *pdata = NULL;
+ struct imx_ahci_priv *imxpriv;
+ struct device *ahci_dev;
+ struct platform_device *ahci_pdev;
+ int ret;
+
+ imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL);
+ if (!imxpriv) {
+ dev_err(dev, "can't alloc ahci_host_priv\n");
+ return -ENOMEM;
+ }
+
+ ahci_pdev = platform_device_alloc("ahci", -1);
+ if (!ahci_pdev)
+ return -ENODEV;
+
+ ahci_dev = &ahci_pdev->dev;
+ ahci_dev->parent = dev;
+
+ imxpriv->ahb_clk = devm_clk_get(dev, "ahb");
+ if (IS_ERR(imxpriv->ahb_clk)) {
+ dev_err(dev, "can't get ahb clock.\n");
+ ret = PTR_ERR(imxpriv->ahb_clk);
+ goto err_out;
+ }
+
+ imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref");
+ if (IS_ERR(imxpriv->sata_ref_clk)) {
+ dev_err(dev, "can't get sata_ref clock.\n");
+ ret = PTR_ERR(imxpriv->sata_ref_clk);
+ goto err_out;
+ }
+
+ imxpriv->ahci_pdev = ahci_pdev;
+ platform_set_drvdata(pdev, imxpriv);
+
+ of_id = of_match_device(imx_ahci_of_match, dev);
+ if (of_id) {
+ pdata = of_id->data;
+ } else {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mem || !irq) {
+ dev_err(dev, "no mmio/irq resource\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ res[0] = *mem;
+ res[1] = *irq;
+
+ ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask;
+ ahci_dev->of_node = dev->of_node;
+
+ ret = platform_device_add_resources(ahci_pdev, res, 2);
+ if (ret)
+ goto err_out;
+
+ ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata));
+ if (ret)
+ goto err_out;
+
+ ret = platform_device_add(ahci_pdev);
+ if (ret) {
+err_out:
+ platform_device_put(ahci_pdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_ahci_remove(struct platform_device *pdev)
+{
+ struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev);
+ struct platform_device *ahci_pdev = imxpriv->ahci_pdev;
+
+ platform_device_unregister(ahci_pdev);
+ return 0;
+}
+
+static struct platform_driver imx_ahci_driver = {
+ .probe = imx_ahci_probe,
+ .remove = imx_ahci_remove,
+ .driver = {
+ .name = "ahci-imx",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_ahci_of_match,
+ },
+};
+module_platform_driver(imx_ahci_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver");
+MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ahci:imx");
--
1.7.10.4

View File

@@ -0,0 +1,24 @@
From: Tejun Heo <tj@kernel.org>
Subject: [PATCH] ahci_imx: depend on CONFIG_MFD_SYSCON
ahci_imx makes use of regmap but the dependency wasn't specified in
Kconfig leading build failures if CONFIG_AHCI_IMX is enabled but
CONFIG_MFD_SYSCON is not. Add the Kconfig dependency.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
drivers/ata/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -99,7 +99,7 @@ config SATA_AHCI_PLATFORM
config AHCI_IMX
tristate "Freescale i.MX AHCI SATA support"
- depends on SATA_AHCI_PLATFORM
+ depends on SATA_AHCI_PLATFORM && MFD_SYSCON
help
This option enables support for the Freescale i.MX SoC's
onboard AHCI SATA.

View File

@@ -0,0 +1,636 @@
--- /dev/null 2013-12-09 21:30:35.000000000 +0000
+++ drivers/pci/host/pcie-designware.h 2013-09-20 01:59:32.000000000 +0000
@@ -0,0 +1,65 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+struct pcie_port_info {
+ u32 cfg0_size;
+ u32 cfg1_size;
+ u32 io_size;
+ u32 mem_size;
+ phys_addr_t io_bus_addr;
+ phys_addr_t mem_bus_addr;
+};
+
+struct pcie_port {
+ struct device *dev;
+ u8 root_bus_nr;
+ void __iomem *dbi_base;
+ u64 cfg0_base;
+ void __iomem *va_cfg0_base;
+ u64 cfg1_base;
+ void __iomem *va_cfg1_base;
+ u64 io_base;
+ u64 mem_base;
+ spinlock_t conf_lock;
+ struct resource cfg;
+ struct resource io;
+ struct resource mem;
+ struct pcie_port_info config;
+ int irq;
+ u32 lanes;
+ struct pcie_host_ops *ops;
+};
+
+struct pcie_host_ops {
+ void (*readl_rc)(struct pcie_port *pp,
+ void __iomem *dbi_base, u32 *val);
+ void (*writel_rc)(struct pcie_port *pp,
+ u32 val, void __iomem *dbi_base);
+ int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
+ int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
+ int (*link_up)(struct pcie_port *pp);
+ void (*host_init)(struct pcie_port *pp);
+};
+
+extern unsigned long global_io_offset;
+
+int cfg_read(void __iomem *addr, int where, int size, u32 *val);
+int cfg_write(void __iomem *addr, int where, int size, u32 val);
+int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val);
+int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val);
+int dw_pcie_link_up(struct pcie_port *pp);
+void dw_pcie_setup_rc(struct pcie_port *pp);
+int dw_pcie_host_init(struct pcie_port *pp);
+int dw_pcie_setup(int nr, struct pci_sys_data *sys);
+struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys);
+int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
--- /dev/null 2013-12-09 21:30:35.000000000 +0000
+++ drivers/pci/host/pcie-designware.c 2013-09-20 01:59:32.000000000 +0000
@@ -0,0 +1,565 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+/* Synopsis specific PCIE configuration registers */
+#define PCIE_PORT_LINK_CONTROL 0x710
+#define PORT_LINK_MODE_MASK (0x3f << 16)
+#define PORT_LINK_MODE_1_LANES (0x1 << 16)
+#define PORT_LINK_MODE_2_LANES (0x3 << 16)
+#define PORT_LINK_MODE_4_LANES (0x7 << 16)
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8)
+#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
+#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
+#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
+
+#define PCIE_MSI_ADDR_LO 0x820
+#define PCIE_MSI_ADDR_HI 0x824
+#define PCIE_MSI_INTR0_ENABLE 0x828
+#define PCIE_MSI_INTR0_MASK 0x82C
+#define PCIE_MSI_INTR0_STATUS 0x830
+
+#define PCIE_ATU_VIEWPORT 0x900
+#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
+#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
+#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
+#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
+#define PCIE_ATU_CR1 0x904
+#define PCIE_ATU_TYPE_MEM (0x0 << 0)
+#define PCIE_ATU_TYPE_IO (0x2 << 0)
+#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
+#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
+#define PCIE_ATU_CR2 0x908
+#define PCIE_ATU_ENABLE (0x1 << 31)
+#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
+#define PCIE_ATU_LOWER_BASE 0x90C
+#define PCIE_ATU_UPPER_BASE 0x910
+#define PCIE_ATU_LIMIT 0x914
+#define PCIE_ATU_LOWER_TARGET 0x918
+#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
+#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
+#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
+#define PCIE_ATU_UPPER_TARGET 0x91C
+
+static struct hw_pci dw_pci;
+
+unsigned long global_io_offset;
+
+static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+int cfg_read(void __iomem *addr, int where, int size, u32 *val)
+{
+ *val = readl(addr);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+ else if (size != 4)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int cfg_write(void __iomem *addr, int where, int size, u32 val)
+{
+ if (size == 4)
+ writel(val, addr);
+ else if (size == 2)
+ writew(val, addr + (where & 2));
+ else if (size == 1)
+ writeb(val, addr + (where & 3));
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val)
+{
+ if (pp->ops->readl_rc)
+ pp->ops->readl_rc(pp, pp->dbi_base + reg, val);
+ else
+ *val = readl(pp->dbi_base + reg);
+}
+
+static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
+{
+ if (pp->ops->writel_rc)
+ pp->ops->writel_rc(pp, val, pp->dbi_base + reg);
+ else
+ writel(val, pp->dbi_base + reg);
+}
+
+int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+ u32 *val)
+{
+ int ret;
+
+ if (pp->ops->rd_own_conf)
+ ret = pp->ops->rd_own_conf(pp, where, size, val);
+ else
+ ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
+
+ return ret;
+}
+
+int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+ u32 val)
+{
+ int ret;
+
+ if (pp->ops->wr_own_conf)
+ ret = pp->ops->wr_own_conf(pp, where, size, val);
+ else
+ ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size,
+ val);
+
+ return ret;
+}
+
+int dw_pcie_link_up(struct pcie_port *pp)
+{
+ if (pp->ops->link_up)
+ return pp->ops->link_up(pp);
+ else
+ return 0;
+}
+
+int __init dw_pcie_host_init(struct pcie_port *pp)
+{
+ struct device_node *np = pp->dev->of_node;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ u32 val;
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(pp->dev, "missing ranges property\n");
+ return -EINVAL;
+ }
+
+ /* Get the I/O and memory ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
+ if (restype == IORESOURCE_IO) {
+ of_pci_range_to_resource(&range, np, &pp->io);
+ pp->io.name = "I/O";
+ pp->io.start = max_t(resource_size_t,
+ PCIBIOS_MIN_IO,
+ range.pci_addr + global_io_offset);
+ pp->io.end = min_t(resource_size_t,
+ IO_SPACE_LIMIT,
+ range.pci_addr + range.size
+ + global_io_offset);
+ pp->config.io_size = resource_size(&pp->io);
+ pp->config.io_bus_addr = range.pci_addr;
+ }
+ if (restype == IORESOURCE_MEM) {
+ of_pci_range_to_resource(&range, np, &pp->mem);
+ pp->mem.name = "MEM";
+ pp->config.mem_size = resource_size(&pp->mem);
+ pp->config.mem_bus_addr = range.pci_addr;
+ }
+ if (restype == 0) {
+ of_pci_range_to_resource(&range, np, &pp->cfg);
+ pp->config.cfg0_size = resource_size(&pp->cfg)/2;
+ pp->config.cfg1_size = resource_size(&pp->cfg)/2;
+ }
+ }
+
+ if (!pp->dbi_base) {
+ pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
+ resource_size(&pp->cfg));
+ if (!pp->dbi_base) {
+ dev_err(pp->dev, "error with ioremap\n");
+ return -ENOMEM;
+ }
+ }
+
+ pp->cfg0_base = pp->cfg.start;
+ pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size;
+ pp->io_base = pp->io.start;
+ pp->mem_base = pp->mem.start;
+
+ pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
+ pp->config.cfg0_size);
+ if (!pp->va_cfg0_base) {
+ dev_err(pp->dev, "error with ioremap in function\n");
+ return -ENOMEM;
+ }
+ pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
+ pp->config.cfg1_size);
+ if (!pp->va_cfg1_base) {
+ dev_err(pp->dev, "error with ioremap\n");
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
+ dev_err(pp->dev, "Failed to parse the number of lanes\n");
+ return -EINVAL;
+ }
+
+ if (pp->ops->host_init)
+ pp->ops->host_init(pp);
+
+ dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
+
+ /* program correct class for RC */
+ dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
+
+ dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
+ val |= PORT_LOGIC_SPEED_CHANGE;
+ dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
+
+ dw_pci.nr_controllers = 1;
+ dw_pci.private_data = (void **)&pp;
+
+ pci_common_init(&dw_pci);
+ pci_assign_unassigned_resources();
+#ifdef CONFIG_PCI_DOMAINS
+ dw_pci.domain++;
+#endif
+
+ return 0;
+}
+
+static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
+{
+ /* Program viewport 0 : OUTBOUND : CFG0 */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+}
+
+static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
+{
+ /* Program viewport 1 : OUTBOUND : CFG1 */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+ dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
+}
+
+static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
+{
+ /* Program viewport 0 : OUTBOUND : MEM */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+ dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
+ PCIE_ATU_UPPER_TARGET);
+}
+
+static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
+{
+ /* Program viewport 1 : OUTBOUND : IO */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+ dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
+ PCIE_ATU_UPPER_TARGET);
+}
+
+static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 *val)
+{
+ int ret = PCIBIOS_SUCCESSFUL;
+ u32 address, busdev;
+
+ busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
+ PCIE_ATU_FUNC(PCI_FUNC(devfn));
+ address = where & ~0x3;
+
+ if (bus->parent->number == pp->root_bus_nr) {
+ dw_pcie_prog_viewport_cfg0(pp, busdev);
+ ret = cfg_read(pp->va_cfg0_base + address, where, size, val);
+ dw_pcie_prog_viewport_mem_outbound(pp);
+ } else {
+ dw_pcie_prog_viewport_cfg1(pp, busdev);
+ ret = cfg_read(pp->va_cfg1_base + address, where, size, val);
+ dw_pcie_prog_viewport_io_outbound(pp);
+ }
+
+ return ret;
+}
+
+static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 val)
+{
+ int ret = PCIBIOS_SUCCESSFUL;
+ u32 address, busdev;
+
+ busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
+ PCIE_ATU_FUNC(PCI_FUNC(devfn));
+ address = where & ~0x3;
+
+ if (bus->parent->number == pp->root_bus_nr) {
+ dw_pcie_prog_viewport_cfg0(pp, busdev);
+ ret = cfg_write(pp->va_cfg0_base + address, where, size, val);
+ dw_pcie_prog_viewport_mem_outbound(pp);
+ } else {
+ dw_pcie_prog_viewport_cfg1(pp, busdev);
+ ret = cfg_write(pp->va_cfg1_base + address, where, size, val);
+ dw_pcie_prog_viewport_io_outbound(pp);
+ }
+
+ return ret;
+}
+
+
+static int dw_pcie_valid_config(struct pcie_port *pp,
+ struct pci_bus *bus, int dev)
+{
+ /* If there is no link, then there is no device */
+ if (bus->number != pp->root_bus_nr) {
+ if (!dw_pcie_link_up(pp))
+ return 0;
+ }
+
+ /* access only one slot on each root port */
+ if (bus->number == pp->root_bus_nr && dev > 0)
+ return 0;
+
+ /*
+ * do not read more than one device on the bus directly attached
+ * to RC's (Virtual Bridge's) DS side.
+ */
+ if (bus->primary == pp->root_bus_nr && dev > 0)
+ return 0;
+
+ return 1;
+}
+
+static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ struct pcie_port *pp = sys_to_pcie(bus->sysdata);
+ unsigned long flags;
+ int ret;
+
+ if (!pp) {
+ BUG();
+ return -EINVAL;
+ }
+
+ if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ spin_lock_irqsave(&pp->conf_lock, flags);
+ if (bus->number != pp->root_bus_nr)
+ ret = dw_pcie_rd_other_conf(pp, bus, devfn,
+ where, size, val);
+ else
+ ret = dw_pcie_rd_own_conf(pp, where, size, val);
+ spin_unlock_irqrestore(&pp->conf_lock, flags);
+
+ return ret;
+}
+
+static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ struct pcie_port *pp = sys_to_pcie(bus->sysdata);
+ unsigned long flags;
+ int ret;
+
+ if (!pp) {
+ BUG();
+ return -EINVAL;
+ }
+
+ if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ spin_lock_irqsave(&pp->conf_lock, flags);
+ if (bus->number != pp->root_bus_nr)
+ ret = dw_pcie_wr_other_conf(pp, bus, devfn,
+ where, size, val);
+ else
+ ret = dw_pcie_wr_own_conf(pp, where, size, val);
+ spin_unlock_irqrestore(&pp->conf_lock, flags);
+
+ return ret;
+}
+
+static struct pci_ops dw_pcie_ops = {
+ .read = dw_pcie_rd_conf,
+ .write = dw_pcie_wr_conf,
+};
+
+int dw_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct pcie_port *pp;
+
+ pp = sys_to_pcie(sys);
+
+ if (!pp)
+ return 0;
+
+ if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
+ sys->io_offset = global_io_offset - pp->config.io_bus_addr;
+ pci_ioremap_io(sys->io_offset, pp->io.start);
+ global_io_offset += SZ_64K;
+ pci_add_resource_offset(&sys->resources, &pp->io,
+ sys->io_offset);
+ }
+
+ sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
+ pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
+
+ return 1;
+}
+
+struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct pci_bus *bus;
+ struct pcie_port *pp = sys_to_pcie(sys);
+
+ if (pp) {
+ pp->root_bus_nr = sys->busnr;
+ bus = pci_scan_root_bus(NULL, sys->busnr, &dw_pcie_ops,
+ sys, &sys->resources);
+ } else {
+ bus = NULL;
+ BUG();
+ }
+
+ return bus;
+}
+
+int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata);
+
+ return pp->irq;
+}
+
+static struct hw_pci dw_pci = {
+ .setup = dw_pcie_setup,
+ .scan = dw_pcie_scan_bus,
+ .map_irq = dw_pcie_map_irq,
+};
+
+void dw_pcie_setup_rc(struct pcie_port *pp)
+{
+ struct pcie_port_info *config = &pp->config;
+ u32 val;
+ u32 membase;
+ u32 memlimit;
+
+ /* set the number of lines as 4 */
+ dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
+ val &= ~PORT_LINK_MODE_MASK;
+ switch (pp->lanes) {
+ case 1:
+ val |= PORT_LINK_MODE_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LINK_MODE_2_LANES;
+ break;
+ case 4:
+ val |= PORT_LINK_MODE_4_LANES;
+ break;
+ }
+ dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
+
+ /* set link width speed control register */
+ dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
+ val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+ switch (pp->lanes) {
+ case 1:
+ val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+ break;
+ case 4:
+ val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
+ break;
+ }
+ dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
+
+ /* setup RC BARs */
+ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0);
+ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_1);
+
+ /* setup interrupt pins */
+ dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val);
+ val &= 0xffff00ff;
+ val |= 0x00000100;
+ dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
+
+ /* setup bus numbers */
+ dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
+ val &= 0xff000000;
+ val |= 0x00010100;
+ dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
+
+ /* setup memory base, memory limit */
+ membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
+ memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000;
+ val = memlimit | membase;
+ dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
+
+ /* setup command register */
+ dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
+ val &= 0xffff0000;
+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+ dw_pcie_writel_rc(pp, val, PCI_COMMAND);
+}
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Designware PCIe host controller driver");
+MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,17 @@
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -125,3 +125,5 @@ config PCI_IOAPIC
config PCI_LABEL
def_bool y if (DMI || ACPI)
select NLS
+
+source "drivers/pci/host/Kconfig"
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -67,3 +67,6 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen
obj-$(CONFIG_OF) += of.o
ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
+
+# PCI host controller drivers
+obj-y += host/

View File

@@ -0,0 +1,194 @@
From: Andrew Murray <Andrew.Murray@arm.com>
Subject: [PATCH] of/pci: Provide support for parsing PCI DT ranges property
This patch factors out common implementation patterns to reduce overall kernel
code and provide a means for host bridge drivers to directly obtain struct
resources from the DT's ranges property without relying on architecture specific
DT handling. This will make it easier to write archiecture independent host bridge
drivers and mitigate against further duplication of DT parsing code.
This patch can be used in the following way:
struct of_pci_range_parser parser;
struct of_pci_range range;
if (of_pci_range_parser_init(&parser, np))
; //no ranges property
for_each_of_pci_range(&parser, &range) {
/*
directly access properties of the address range, e.g.:
range.pci_space, range.pci_addr, range.cpu_addr,
range.size, range.flags
alternatively obtain a struct resource, e.g.:
struct resource res;
of_pci_range_to_resource(&range, np, &res);
*/
}
Additionally the implementation takes care of adjacent ranges and merges them
into a single range (as was the case with powerpc and microblaze).
Signed-off-by: Andrew Murray <Andrew.Murray@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Reviewed-by: Rob Herring <rob.herring@calxeda.com>
Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Jingoo Han <jg1.han@samsung.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
---
drivers/of/address.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/of_address.h | 48 +++++++++++++++++++++++++++++++++
2 files changed, 115 insertions(+)
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -231,6 +231,73 @@ int of_pci_address_to_resource(struct de
return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
}
EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
+
+int of_pci_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ const int na = 3, ns = 2;
+ int rlen;
+
+ parser->node = node;
+ parser->pna = of_n_addr_cells(node);
+ parser->np = parser->pna + na + ns;
+
+ parser->range = of_get_property(node, "ranges", &rlen);
+ if (parser->range == NULL)
+ return -ENOENT;
+
+ parser->end = parser->range + rlen / sizeof(__be32);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pci_range_parser_init);
+
+struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
+ struct of_pci_range *range)
+{
+ const int na = 3, ns = 2;
+
+ if (!range)
+ return NULL;
+
+ if (!parser->range || parser->range + parser->np > parser->end)
+ return NULL;
+
+ range->pci_space = parser->range[0];
+ range->flags = of_bus_pci_get_flags(parser->range);
+ range->pci_addr = of_read_number(parser->range + 1, ns);
+ range->cpu_addr = of_translate_address(parser->node,
+ parser->range + na);
+ range->size = of_read_number(parser->range + parser->pna + na, ns);
+
+ parser->range += parser->np;
+
+ /* Now consume following elements while they are contiguous */
+ while (parser->range + parser->np <= parser->end) {
+ u32 flags, pci_space;
+ u64 pci_addr, cpu_addr, size;
+
+ pci_space = be32_to_cpup(parser->range);
+ flags = of_bus_pci_get_flags(parser->range);
+ pci_addr = of_read_number(parser->range + 1, ns);
+ cpu_addr = of_translate_address(parser->node,
+ parser->range + na);
+ size = of_read_number(parser->range + parser->pna + na, ns);
+
+ if (flags != range->flags)
+ break;
+ if (pci_addr != range->pci_addr + range->size ||
+ cpu_addr != range->cpu_addr + range->size)
+ break;
+
+ range->size += size;
+ parser->range += parser->np;
+ }
+
+ return range;
+}
+EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
+
#endif /* CONFIG_PCI */
/*
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -4,6 +4,36 @@
#include <linux/errno.h>
#include <linux/of.h>
+struct of_pci_range_parser {
+ struct device_node *node;
+ const __be32 *range;
+ const __be32 *end;
+ int np;
+ int pna;
+};
+
+struct of_pci_range {
+ u32 pci_space;
+ u64 pci_addr;
+ u64 cpu_addr;
+ u64 size;
+ u32 flags;
+};
+
+#define for_each_of_pci_range(parser, range) \
+ for (; of_pci_range_parser_one(parser, range);)
+
+static inline void of_pci_range_to_resource(struct of_pci_range *range,
+ struct device_node *np,
+ struct resource *res)
+{
+ res->flags = range->flags;
+ res->start = range->cpu_addr;
+ res->end = range->cpu_addr + range->size - 1;
+ res->parent = res->child = res->sibling = NULL;
+ res->name = np->full_name;
+}
+
#ifdef CONFIG_OF_ADDRESS
extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
extern bool of_can_translate_address(struct device_node *dev);
@@ -27,6 +57,11 @@ static inline unsigned long pci_address_
#define pci_address_to_pio pci_address_to_pio
#endif
+extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node);
+extern struct of_pci_range *of_pci_range_parser_one(
+ struct of_pci_range_parser *parser,
+ struct of_pci_range *range);
#else /* CONFIG_OF_ADDRESS */
#ifndef of_address_to_resource
static inline int of_address_to_resource(struct device_node *dev, int index,
@@ -53,6 +88,19 @@ static inline const __be32 *of_get_addre
{
return NULL;
}
+
+static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ return -1;
+}
+
+static inline struct of_pci_range *of_pci_range_parser_one(
+ struct of_pci_range_parser *parser,
+ struct of_pci_range *range)
+{
+ return NULL;
+}
#endif /* CONFIG_OF_ADDRESS */

View File

@@ -0,0 +1,37 @@
From: Sean Cross <xobs@kosagi.com>
Subject: [PATCH 1/2] ARM: imx6q: Add PCIe bits to GPR syscon definition
PCIe requires additional bits be defined for GPR8 and GPR12.
Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
@@ -241,6 +241,12 @@
#define IMX6Q_GPR5_L2_CLK_STOP BIT(8)
+#define IMX6Q_GPR8_TX_SWING_LOW (0x7f << 25)
+#define IMX6Q_GPR8_TX_SWING_FULL (0x7f << 18)
+#define IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB (0x3f << 12)
+#define IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB (0x3f << 6)
+#define IMX6Q_GPR8_TX_DEEMPH_GEN1 (0x3f << 0)
+
#define IMX6Q_GPR9_TZASC2_BYP BIT(1)
#define IMX6Q_GPR9_TZASC1_BYP BIT(0)
@@ -273,7 +279,9 @@
#define IMX6Q_GPR12_ARMP_AHB_CLK_EN BIT(26)
#define IMX6Q_GPR12_ARMP_ATB_CLK_EN BIT(25)
#define IMX6Q_GPR12_ARMP_APB_CLK_EN BIT(24)
+#define IMX6Q_GPR12_DEVICE_TYPE (0xf << 12)
#define IMX6Q_GPR12_PCIE_CTL_2 BIT(10)
+#define IMX6Q_GPR12_LOS_LEVEL (0x1f << 4)
#define IMX6Q_GPR13_SDMA_STOP_REQ BIT(30)
#define IMX6Q_GPR13_CAN2_STOP_REQ BIT(29)

View File

@@ -0,0 +1,616 @@
Subject: [PATCH 2/2] PCI: imx6: Add support for i.MX6 PCIe controller
From: Sean Cross <xobs@kosagi.com>
Add support for the PCIe port present on the i.MX6 family of controllers.
These use the Synopsis Designware core tied to their own PHY.
Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/pci/host/Kconfig | 6 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-imx6.c | 575 +++++++++++++++++++++
4 files changed, 588 insertions(+), 1 deletion(-)
create mode 100644 drivers/pci/host/pci-imx6.c
--- /dev/null
+++ b/drivers/pci/host/Kconfig
@@ -0,0 +1,13 @@
+menu "PCI host controller drivers"
+ depends on PCI
+
+config PCIE_DW
+ bool
+
+config PCI_IMX6
+ bool "Freescale i.MX6 PCIe controller"
+ depends on SOC_IMX6Q
+ select PCIEPORTBUS
+ select PCIE_DW
+
+endmenu
--- /dev/null
+++ b/drivers/pci/host/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
--- /dev/null
+++ b/drivers/pci/host/pci-imx6.c
@@ -0,0 +1,575 @@
+/*
+ * PCIe host controller driver for Freescale i.MX6 SoCs
+ *
+ * Copyright (C) 2013 Kosagi
+ * http://www.kosagi.com
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
+
+struct imx6_pcie {
+ int reset_gpio;
+ int power_on_gpio;
+ int wake_up_gpio;
+ int disable_gpio;
+ struct clk *lvds_gate;
+ struct clk *sata_ref_100m;
+ struct clk *pcie_ref_125m;
+ struct clk *pcie_axi;
+ struct pcie_port pp;
+ struct regmap *iomuxc_gpr;
+ void __iomem *mem_base;
+};
+
+/* PCIe Port Logic registers (memory-mapped) */
+#define PL_OFFSET 0x700
+#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
+#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
+
+#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
+#define PCIE_PHY_CTRL_DATA_LOC 0
+#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
+#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
+#define PCIE_PHY_CTRL_WR_LOC 18
+#define PCIE_PHY_CTRL_RD_LOC 19
+
+#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
+#define PCIE_PHY_STAT_ACK_LOC 16
+
+/* PHY registers (not memory-mapped) */
+#define PCIE_PHY_RX_ASIC_OUT 0x100D
+
+#define PHY_RX_OVRD_IN_LO 0x1005
+#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
+#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
+
+static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
+{
+ u32 val;
+ u32 max_iterations = 10;
+ u32 wait_counter = 0;
+
+ do {
+ val = readl(dbi_base + PCIE_PHY_STAT);
+ val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
+ wait_counter++;
+
+ if (val == exp_val)
+ return 0;
+
+ udelay(1);
+ } while (wait_counter < max_iterations);
+
+ return -ETIMEDOUT;
+}
+
+static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
+{
+ u32 val;
+ int ret;
+
+ val = addr << PCIE_PHY_CTRL_DATA_LOC;
+ writel(val, dbi_base + PCIE_PHY_CTRL);
+
+ val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
+ writel(val, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ val = addr << PCIE_PHY_CTRL_DATA_LOC;
+ writel(val, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
+static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
+{
+ u32 val, phy_ctl;
+ int ret;
+
+ ret = pcie_phy_wait_ack(dbi_base, addr);
+ if (ret)
+ return ret;
+
+ /* assert Read signal */
+ phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
+ writel(phy_ctl, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ val = readl(dbi_base + PCIE_PHY_STAT);
+ *data = val & 0xffff;
+
+ /* deassert Read signal */
+ writel(0x00, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
+{
+ u32 var;
+ int ret;
+
+ /* write addr */
+ /* cap addr */
+ ret = pcie_phy_wait_ack(dbi_base, addr);
+ if (ret)
+ return ret;
+
+ var = data << PCIE_PHY_CTRL_DATA_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* capture data */
+ var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ /* deassert cap data */
+ var = data << PCIE_PHY_CTRL_DATA_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* wait for ack de-assertion */
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ /* assert wr signal */
+ var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* wait for ack */
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ /* deassert wr signal */
+ var = data << PCIE_PHY_CTRL_DATA_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* wait for ack de-assertion */
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ writel(0x0, dbi_base + PCIE_PHY_CTRL);
+
+ return 0;
+}
+
+/* Added for PCI abort handling */
+static int imx6q_pcie_abort_handler(unsigned long addr,
+ unsigned int fsr, struct pt_regs *regs)
+{
+ /*
+ * If it was an imprecise abort, then we need to correct the
+ * return address to be _after_ the instruction.
+ */
+ if (fsr & (1 << 10))
+ regs->ARM_pc += 4;
+ return 0;
+}
+
+static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
+
+ gpio_set_value(imx6_pcie->reset_gpio, 0);
+ msleep(100);
+ gpio_set_value(imx6_pcie->reset_gpio, 1);
+
+ return 0;
+}
+
+static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+ int ret;
+
+ if (gpio_is_valid(imx6_pcie->power_on_gpio))
+ gpio_set_value(imx6_pcie->power_on_gpio, 1);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+
+ ret = clk_prepare_enable(imx6_pcie->sata_ref_100m);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable sata_ref_100m\n");
+ goto err_sata_ref;
+ }
+
+ ret = clk_prepare_enable(imx6_pcie->pcie_ref_125m);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable pcie_ref_125m\n");
+ goto err_pcie_ref;
+ }
+
+ ret = clk_prepare_enable(imx6_pcie->lvds_gate);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable lvds_gate\n");
+ goto err_lvds_gate;
+ }
+
+ ret = clk_prepare_enable(imx6_pcie->pcie_axi);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable pcie_axi\n");
+ goto err_pcie_axi;
+ }
+
+ /* allow the clocks to stabilize */
+ usleep_range(200, 500);
+
+ return 0;
+
+err_pcie_axi:
+ clk_disable_unprepare(imx6_pcie->lvds_gate);
+err_lvds_gate:
+ clk_disable_unprepare(imx6_pcie->pcie_ref_125m);
+err_pcie_ref:
+ clk_disable_unprepare(imx6_pcie->sata_ref_100m);
+err_sata_ref:
+ return ret;
+
+}
+
+static void imx6_pcie_init_phy(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
+
+ /* configure constant input signal to the pcie ctrl and phy */
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, 0 << 6);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, 20 << 12);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_SWING_FULL, 127 << 18);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_SWING_LOW, 127 << 25);
+}
+
+static void imx6_pcie_host_init(struct pcie_port *pp)
+{
+ int count = 0;
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+
+ imx6_pcie_assert_core_reset(pp);
+
+ imx6_pcie_init_phy(pp);
+
+ imx6_pcie_deassert_core_reset(pp);
+
+ dw_pcie_setup_rc(pp);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+
+ while (!dw_pcie_link_up(pp)) {
+ usleep_range(100, 1000);
+ count++;
+ if (count >= 10) {
+ dev_err(pp->dev, "phy link never came up\n");
+ dev_dbg(pp->dev,
+ "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+ break;
+ }
+ }
+
+ return;
+}
+
+static int imx6_pcie_link_up(struct pcie_port *pp)
+{
+ u32 rc, ltssm, rx_valid, temp;
+
+ /* link is debug bit 36, debug register 1 starts at bit 32 */
+ rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32));
+ if (rc)
+ return -EAGAIN;
+
+ /*
+ * From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
+ * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
+ * If (MAC/LTSSM.state == Recovery.RcvrLock)
+ * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
+ * to gen2 is stuck
+ */
+ pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
+ ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F;
+
+ if (rx_valid & 0x01)
+ return 0;
+
+ if (ltssm != 0x0d)
+ return 0;
+
+ dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n");
+
+ pcie_phy_read(pp->dbi_base,
+ PHY_RX_OVRD_IN_LO, &temp);
+ temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN
+ | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+ pcie_phy_write(pp->dbi_base,
+ PHY_RX_OVRD_IN_LO, temp);
+
+ usleep_range(2000, 3000);
+
+ pcie_phy_read(pp->dbi_base,
+ PHY_RX_OVRD_IN_LO, &temp);
+ temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN
+ | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+ pcie_phy_write(pp->dbi_base,
+ PHY_RX_OVRD_IN_LO, temp);
+
+ return 0;
+}
+
+static struct pcie_host_ops imx6_pcie_host_ops = {
+ .link_up = imx6_pcie_link_up,
+ .host_init = imx6_pcie_host_init,
+};
+
+static int imx6_add_pcie_port(struct pcie_port *pp,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ pp->irq = platform_get_irq(pdev, 0);
+ if (!pp->irq) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return -ENODEV;
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &imx6_pcie_host_ops;
+
+ spin_lock_init(&pp->conf_lock);
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init imx6_pcie_probe(struct platform_device *pdev)
+{
+ struct imx6_pcie *imx6_pcie;
+ struct pcie_port *pp;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *dbi_base;
+ int ret;
+
+ imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
+ if (!imx6_pcie)
+ return -ENOMEM;
+
+ pp = &imx6_pcie->pp;
+ pp->dev = &pdev->dev;
+
+ /* Added for PCI abort handling */
+ hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
+ "imprecise external abort");
+
+ dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!dbi_base) {
+ dev_err(&pdev->dev, "dbi_base memory resource not found\n");
+ return -ENODEV;
+ }
+
+ pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+ if (IS_ERR(pp->dbi_base)) {
+ dev_err(&pdev->dev, "unable to remap dbi_base\n");
+ ret = PTR_ERR(pp->dbi_base);
+ goto err;
+ }
+
+ /* Fetch GPIOs */
+ imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+ if (!gpio_is_valid(imx6_pcie->reset_gpio)) {
+ dev_err(&pdev->dev, "no reset-gpio defined\n");
+ ret = -ENODEV;
+ }
+ ret = devm_gpio_request_one(&pdev->dev,
+ imx6_pcie->reset_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "PCIe reset");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get reset gpio\n");
+ goto err;
+ }
+
+ imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0);
+ if (gpio_is_valid(imx6_pcie->power_on_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev,
+ imx6_pcie->power_on_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "PCIe power enable");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get power-on gpio\n");
+ goto err;
+ }
+ }
+
+ imx6_pcie->wake_up_gpio = of_get_named_gpio(np, "wake-up-gpio", 0);
+ if (gpio_is_valid(imx6_pcie->wake_up_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev,
+ imx6_pcie->wake_up_gpio,
+ GPIOF_IN,
+ "PCIe wake up");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get wake-up gpio\n");
+ goto err;
+ }
+ }
+
+ imx6_pcie->disable_gpio = of_get_named_gpio(np, "disable-gpio", 0);
+ if (gpio_is_valid(imx6_pcie->disable_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev,
+ imx6_pcie->disable_gpio,
+ GPIOF_OUT_INIT_HIGH,
+ "PCIe disable endpoint");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get disable-ep gpio\n");
+ goto err;
+ }
+ }
+
+ /* Fetch clocks */
+ imx6_pcie->lvds_gate = devm_clk_get(&pdev->dev, "lvds_gate");
+ if (IS_ERR(imx6_pcie->lvds_gate)) {
+ dev_err(&pdev->dev,
+ "lvds_gate clock select missing or invalid\n");
+ ret = PTR_ERR(imx6_pcie->lvds_gate);
+ goto err;
+ }
+
+ imx6_pcie->sata_ref_100m = devm_clk_get(&pdev->dev, "sata_ref_100m");
+ if (IS_ERR(imx6_pcie->sata_ref_100m)) {
+ dev_err(&pdev->dev,
+ "sata_ref_100m clock source missing or invalid\n");
+ ret = PTR_ERR(imx6_pcie->sata_ref_100m);
+ goto err;
+ }
+
+ imx6_pcie->pcie_ref_125m = devm_clk_get(&pdev->dev, "pcie_ref_125m");
+ if (IS_ERR(imx6_pcie->pcie_ref_125m)) {
+ dev_err(&pdev->dev,
+ "pcie_ref_125m clock source missing or invalid\n");
+ ret = PTR_ERR(imx6_pcie->pcie_ref_125m);
+ goto err;
+ }
+
+ imx6_pcie->pcie_axi = devm_clk_get(&pdev->dev, "pcie_axi");
+ if (IS_ERR(imx6_pcie->pcie_axi)) {
+ dev_err(&pdev->dev,
+ "pcie_axi clock source missing or invalid\n");
+ ret = PTR_ERR(imx6_pcie->pcie_axi);
+ goto err;
+ }
+
+ /* Grab GPR config register range */
+ imx6_pcie->iomuxc_gpr =
+ syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+ if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
+ dev_err(&pdev->dev, "unable to find iomuxc registers\n");
+ ret = PTR_ERR(imx6_pcie->iomuxc_gpr);
+ goto err;
+ }
+
+ ret = imx6_add_pcie_port(pp, pdev);
+ if (ret < 0)
+ goto err;
+
+ platform_set_drvdata(pdev, imx6_pcie);
+ return 0;
+
+err:
+ return ret;
+}
+
+static const struct of_device_id imx6_pcie_of_match[] = {
+ { .compatible = "fsl,imx6q-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
+
+static struct platform_driver imx6_pcie_driver = {
+ .driver = {
+ .name = "imx6q-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(imx6_pcie_of_match),
+ },
+};
+
+/* Freescale PCIe driver does not allow module unload */
+
+static int __init imx6_pcie_init(void)
+{
+ return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
+}
+module_init(imx6_pcie_init);
+
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
+MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,24 @@
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -200,12 +200,6 @@ static int pcie_phy_write(void __iomem *
static int imx6q_pcie_abort_handler(unsigned long addr,
unsigned int fsr, struct pt_regs *regs)
{
- /*
- * If it was an imprecise abort, then we need to correct the
- * return address to be _after_ the instruction.
- */
- if (fsr & (1 << 10))
- regs->ARM_pc += 4;
return 0;
}
@@ -322,7 +316,7 @@ static void imx6_pcie_host_init(struct p
IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
while (!dw_pcie_link_up(pp)) {
- usleep_range(100, 1000);
+ usleep_range(2000, 3000);
count++;
if (count >= 10) {
dev_err(pp->dev, "phy link never came up\n");

View File

@@ -0,0 +1,70 @@
From: Sean Cross <xobs@kosagi.com>
Subject: [PATCH 1/3] ARM: imx: Add LVDS general-purpose clocks to i.MX6Q
The i.MX6 has two general-purpose LVDS clocks that can be driven
from a variety of sources. This patch adds a mux and a gate for
both of these clocks.
Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
.../devicetree/bindings/clock/imx6q-clock.txt | 4 ++++
arch/arm/mach-imx/clk-imx6q.c | 20 +++++++++++++++++++-
2 files changed, 23 insertions(+), 1 deletion(-)
--- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
@@ -208,6 +208,10 @@ clocks and IDs.
pll4_post_div 193
pll5_post_div 194
pll5_video_div 195
+ lvds1_sel 204
+ lvds2_sel 205
+ lvds1_gate 206
+ lvds2_gate 207
Examples:
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -205,6 +205,11 @@ static const char *vpu_axi_sels[] = { "a
static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div",
"dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0",
"ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_post_div", };
+static const char *lvds_sels[] = {
+ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
+ "pll4_audio", "pll5_video", "pll8_mlb", "enet_ref",
+ "pcie_ref", "sata_ref",
+};
enum mx6q_clks {
dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m,
@@ -238,7 +243,8 @@ enum mx6q_clks {
pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg,
ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
- usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, clk_max
+ usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div,
+ lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, clk_max
};
static struct clk *clk[clk_max];
@@ -340,6 +346,18 @@ int __init mx6q_clocks_init(void)
base + 0xe0, 0, 2, 0, clk_enet_ref_table,
&imx_ccm_lock);
+ clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels));
+ clk[lvds2_sel] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels));
+
+ /*
+ * lvds1_gate and lvds2_gate are pseudo-gates. Both can be
+ * independently configured as clock inputs or outputs. We treat
+ * the "output_enable" bit as a gate, even though it's really just
+ * enabling clock output.
+ */
+ clk[lvds1_gate] = imx_clk_gate("lvds1_gate", "dummy", base + 0x160, 10);
+ clk[lvds2_gate] = imx_clk_gate("lvds2_gate", "dummy", base + 0x160, 11);
+
/* name parent_name reg idx */
clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0);
clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1);

View File

@@ -0,0 +1,38 @@
From 4f6723e8ff497e35c8f2fb20886fccc533c58cdb Mon Sep 17 00:00:00 2001
From: Sean Cross <xobs@kosagi.com>
Date: Thu, 26 Sep 2013 10:45:35 +0800
Subject: [PATCH] ARM: imx6q: clock and Kconfig update for PCIe support
Update imx6q clock initialization and Kconfig for PCIe support.
Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
arch/arm/mach-imx/Kconfig | 2 ++
arch/arm/mach-imx/clk-imx6q.c | 4 ++++
2 files changed, 6 insertions(+)
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -806,6 +806,8 @@ config SOC_IMX6Q
select HAVE_IMX_SRC
select HAVE_SMP
select MFD_SYSCON
+ select MIGHT_HAVE_PCI
+ select PCI_DOMAINS if PCI
select PINCTRL
select PINCTRL_IMX6Q
select PL310_ERRATA_588369 if CACHE_PL310
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -586,6 +586,10 @@ int __init mx6q_clocks_init(void)
clk_prepare_enable(clk[usbphy2_gate]);
}
+ /* All existing boards with PCIe use LVDS1 */
+ if (IS_ENABLED(CONFIG_PCI_IMX6))
+ clk_set_parent(clk[lvds1_sel], clk[sata_ref]);
+
/* Set initial power mode */
imx6q_set_lpm(WAIT_CLOCKED);