Update Centec platform support for Bullseye and 5.10 kernel (#7)

1. Fix build for armhf and arm64
2. upgrade centec tsingma bsp support to 5.10 kernel
3. modify centec platform driver for linux 5.10

Co-authored-by: Shi Lei <shil@centecnetworks.com>
This commit is contained in:
LuiSzee 2021-10-26 06:36:55 +08:00 коммит произвёл Saikrishna Arcot
Родитель b4dda1c18d
Коммит 5b284767f6
80 изменённых файлов: 8261 добавлений и 8076 удалений

Просмотреть файл

@ -1,13 +1,13 @@
## Debian mirror for ARM
## Not the repo mirror site can change in future, and needs to be updated to be in sync
deb [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
deb [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
deb [arch=arm64] http://ftp.debian.org/debian buster-backports main
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-backports main
deb [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=arm64] http://ftp.debian.org/debian bullseye-backports main
# deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
# deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
# deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
# deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
# deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye-backports main

Просмотреть файл

@ -1,13 +1,13 @@
## Debian mirror for ARM
## Not the repo mirror site can change in future, and needs to be updated to be in sync
deb [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
deb [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
deb [arch=armhf] http://ftp.debian.org/debian buster-backports main
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-backports main
deb [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=armhf] http://ftp.debian.org/debian bullseye-backports main
# deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
# deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
# deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
# deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
# deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye-backports main

Просмотреть файл

@ -643,7 +643,7 @@ clean_proc() {
sudo umount /proc || true
}
trap_push clean_proc
sudo mount proc /proc -t proc
sudo mount proc /proc -t proc || true
sudo mkdir $FILESYSTEM_ROOT/target
sudo mount --bind target $FILESYSTEM_ROOT/target
sudo LANG=C DOCKER_HOST="$DOCKER_HOST" chroot $FILESYSTEM_ROOT docker info

Просмотреть файл

@ -1,9 +1,9 @@
deb [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
deb [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
deb [arch=arm64] http://security.debian.org buster/updates main contrib non-free
deb-src [arch=arm64] http://security.debian.org buster/updates main contrib non-free
deb [arch=arm64] https://download.docker.com/linux/debian buster stable
deb [arch=arm64] http://ftp.debian.org/debian buster-backports main
deb [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=arm64] http://security.debian.org bullseye-security main contrib non-free
deb-src [arch=arm64] http://security.debian.org bullseye-security main contrib non-free
deb [arch=arm64] https://download.docker.com/linux/debian bullseye stable
deb [arch=arm64] http://ftp.debian.org/debian bullseye-backports main

Просмотреть файл

@ -1,9 +1,9 @@
deb [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
deb [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
deb [arch=armhf] http://security.debian.org buster/updates main contrib non-free
deb-src [arch=armhf] http://security.debian.org buster/updates main contrib non-free
deb [arch=armhf] https://download.docker.com/linux/debian buster stable
deb [arch=armhf] http://ftp.debian.org/debian buster-backports main
deb [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=armhf] http://security.debian.org bullseye-security main contrib non-free
deb-src [arch=armhf] http://security.debian.org bullseye-security main contrib non-free
deb [arch=armhf] https://download.docker.com/linux/debian bullseye stable
deb [arch=armhf] http://ftp.debian.org/debian bullseye-backports main

Просмотреть файл

@ -10,7 +10,6 @@ rtc-sd2405
ctc5236_switch
ctc5236_mdio
ctcmac
ctcmac_test
ctc5236-mc
ctc_wdt
ehci-ctc
@ -20,3 +19,4 @@ pwm-ctc
ext4
overlay
squashfs
mars

Просмотреть файл

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_24x2c_platform.o

Просмотреть файл

@ -1,7 +1,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/leds.h>
@ -129,7 +128,7 @@ static int e530_24x2c_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0);
i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0))
{
i2c_client_gpio0 = NULL;
@ -145,7 +144,7 @@ static int e530_24x2c_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1);
i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1))
{
i2c_client_gpio1 = NULL;
@ -161,7 +160,7 @@ static int e530_24x2c_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio2 = i2c_new_device(i2c_adp_gpio2, &i2c_dev_gpio2);
i2c_client_gpio2 = i2c_new_client_device(i2c_adp_gpio2, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2))
{
i2c_client_gpio2 = NULL;

Просмотреть файл

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_24x2q_platform.o

Просмотреть файл

@ -1,7 +1,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/leds.h>
@ -129,7 +128,7 @@ static int e530_24x2q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0);
i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0))
{
i2c_client_gpio0 = NULL;
@ -145,7 +144,7 @@ static int e530_24x2q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1);
i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1))
{
i2c_client_gpio1 = NULL;
@ -161,7 +160,7 @@ static int e530_24x2q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio2 = i2c_new_device(i2c_adp_gpio2, &i2c_dev_gpio2);
i2c_client_gpio2 = i2c_new_client_device(i2c_adp_gpio2, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2))
{
i2c_client_gpio2 = NULL;

Просмотреть файл

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_48s4x_platform.o

Просмотреть файл

@ -1,7 +1,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/leds.h>
@ -197,7 +196,7 @@ static int e530_48s4x_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0);
i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0))
{
i2c_client_gpio0 = NULL;
@ -213,7 +212,7 @@ static int e530_48s4x_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1);
i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1))
{
i2c_client_gpio1 = NULL;
@ -274,7 +273,7 @@ static int e530_48s4x_init_i2c_epld(void)
return -1;
}
i2c_client_epld = i2c_new_device(i2c_adp_master, &i2c_dev_epld);
i2c_client_epld = i2c_new_client_device(i2c_adp_master, &i2c_dev_epld);
if(IS_INVALID_PTR(i2c_client_epld))
{
i2c_client_epld = NULL;

Просмотреть файл

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_48t4x_p_platform.o

Просмотреть файл

@ -1,7 +1,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/leds.h>
@ -195,7 +194,7 @@ static int e530_48t4x_p_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0);
i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0))
{
i2c_client_gpio0 = NULL;

Просмотреть файл

@ -7,20 +7,20 @@ Standards-Version: 3.9.3
Package: platform-modules-e530-48t4x-p
Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned
Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e530-24x2c
Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned
Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e530-48s4x
Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned
Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e530-24x2q
Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned
Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp

Просмотреть файл

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr
fi
depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_24x2c_platform
modprobe fan-ctc5236
modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal
modprobe -r fan-ctc5236
modprobe -r centec_e530_24x2c_platform
modprobe -r ctc-i2c-mux-pca954x
}
case "$1" in

Просмотреть файл

@ -1,2 +1 @@
24x2c/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2c-r0
24x2c/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2c-r0

Просмотреть файл

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr
fi
depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_24x2q_platform
modprobe fan-ctc5236
modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal
modprobe -r fan-ctc5236
modprobe -r centec_e530_24x2q_platform
modprobe -r ctc-i2c-mux-pca954x
}
case "$1" in

Просмотреть файл

@ -1,2 +1 @@
24x2q/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2q-r0
24x2q/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2q-r0

Просмотреть файл

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr
fi
depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_48s4x_platform
modprobe fan-ctc5236
modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal
modprobe -r fan-ctc5236
modprobe -r centec_e530_48s4x_platform
modprobe -r ctc-i2c-mux-pca954x
}
case "$1" in

Просмотреть файл

@ -1,2 +1 @@
48s4x/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_48s4x-r0
48s4x/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_48s4x-r0

Просмотреть файл

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr
fi
depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_48t4x_p_platform
modprobe fan-ctc5236
modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal
modprobe -r fan-ctc5236
modprobe -r centec_e530_48t4x_p_platform
modprobe -r ctc-i2c-mux-pca954x
}
case "$1" in

Просмотреть файл

@ -1,2 +1 @@
48t4x-p/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_48t4x_p-r0
48t4x-p/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_48t4x_p-r0

Просмотреть файл

@ -24,6 +24,7 @@ CLASSES_DIR := classes
CONF_DIR := conf
KDAL_DIR := ../../centec/centec-dal/
FAN_DIR := fan
PCA954X_DIR := pca954x
%:
dh $@
@ -41,10 +42,12 @@ build:
(for mod in $(FAN_DIR); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
done)
(for mod in $(PCA954X_DIR); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
done)
(for mod in $(MODULE_DIRS); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
cd $${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd -; \
done)
@ -73,6 +76,7 @@ binary-indep:
cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$(KDAL_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$(FAN_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$(PCA954X_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \
done)
# Resuming debhelper scripts

Просмотреть файл

@ -0,0 +1 @@
obj-m := ctc-i2c-mux-pca954x.o

Просмотреть файл

@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0
/*
* I2C multiplexer
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
*
* This module supports the PCA954x and PCA984x series of I2C multiplexer/switch
* chips made by NXP Semiconductors.
* This includes the:
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547,
* PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register. The upstream "parent" bus fans out to two,
* four, or eight downstream busses or channels; which of these
* are selected is determined by the chip type and register contents. A
* mux can select only one sub-bus at a time; a switch can select any
* combination simultaneously.
*
* Based on:
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
* Copyright (C) 2006
*
* Based on:
* pca954x.c from Ken Harrenstien
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <jdelvare@suse.de>.
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <dt-bindings/mux/mux.h>
#include "ctc-pca954x.h"
#define PCA954X_MAX_NCHANS 8
#define PCA954X_IRQ_OFFSET 4
enum pca_type {
pca_9540,
pca_9542,
pca_9543,
pca_9544,
pca_9545,
pca_9546,
pca_9547,
pca_9548,
pca_9846,
pca_9847,
pca_9848,
pca_9849,
};
struct chip_desc {
u8 nchans;
u8 enable; /* used for muxes only */
u8 has_irq;
enum muxtype {
pca954x_ismux = 0,
pca954x_isswi
} muxtype;
struct i2c_device_identity id;
};
struct pca954x {
const struct chip_desc *chip;
u8 last_chan; /* last register value */
/* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
s32 idle_state;
struct i2c_client *client;
struct irq_domain *irq;
unsigned int irq_mask;
raw_spinlock_t lock;
};
/* Provide specs for the PCA954x types we know about */
static const struct chip_desc chips[] = {
[pca_9540] = {
.nchans = 2,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9542] = {
.nchans = 2,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9543] = {
.nchans = 2,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9544] = {
.nchans = 4,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9545] = {
.nchans = 4,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9546] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9547] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9548] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9846] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10b,
},
},
[pca_9847] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x108,
},
},
[pca_9848] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10a,
},
},
[pca_9849] = {
.nchans = 4,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x109,
},
},
};
static const struct i2c_device_id pca954x_id[] = {
{ "ctc_pca9540", pca_9540 },
{ "ctc_pca9542", pca_9542 },
{ "ctc_pca9543", pca_9543 },
{ "ctc_pca9544", pca_9544 },
{ "ctc_pca9545", pca_9545 },
{ "ctc_pca9546", pca_9546 },
{ "ctc_pca9547", pca_9547 },
{ "ctc_pca9548", pca_9548 },
{ "ctc_pca9846", pca_9846 },
{ "ctc_pca9847", pca_9847 },
{ "ctc_pca9848", pca_9848 },
{ "ctc_pca9849", pca_9849 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,ctc_pca9540", .data = &chips[pca_9540] },
{ .compatible = "nxp,ctc_pca9542", .data = &chips[pca_9542] },
{ .compatible = "nxp,ctc_pca9543", .data = &chips[pca_9543] },
{ .compatible = "nxp,ctc_pca9544", .data = &chips[pca_9544] },
{ .compatible = "nxp,ctc_pca9545", .data = &chips[pca_9545] },
{ .compatible = "nxp,ctc_pca9546", .data = &chips[pca_9546] },
{ .compatible = "nxp,ctc_pca9547", .data = &chips[pca_9547] },
{ .compatible = "nxp,ctc_pca9548", .data = &chips[pca_9548] },
{ .compatible = "nxp,ctc_pca9846", .data = &chips[pca_9846] },
{ .compatible = "nxp,ctc_pca9847", .data = &chips[pca_9847] },
{ .compatible = "nxp,ctc_pca9848", .data = &chips[pca_9848] },
{ .compatible = "nxp,ctc_pca9849", .data = &chips[pca_9849] },
{}
};
MODULE_DEVICE_TABLE(of, pca954x_of_match);
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
union i2c_smbus_data dummy;
return __i2c_smbus_xfer(adap, client->addr, client->flags,
I2C_SMBUS_WRITE, val,
I2C_SMBUS_BYTE, &dummy);
}
static u8 pca954x_regval(struct pca954x *data, u8 chan)
{
/* We make switches look like muxes, not sure how to be smarter. */
if (data->chip->muxtype == pca954x_ismux)
return chan | data->chip->enable;
else
return 1 << chan;
}
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
u8 regval;
int ret = 0;
regval = pca954x_regval(data, chan);
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
data->last_chan = ret < 0 ? 0 : regval;
}
return ret;
}
static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
s32 idle_state;
idle_state = READ_ONCE(data->idle_state);
if (idle_state >= 0)
/* Set the mux back to a predetermined channel */
return pca954x_select_chan(muxc, idle_state);
if (idle_state == MUX_IDLE_DISCONNECT) {
/* Deselect active channel */
data->last_chan = 0;
return pca954x_reg_write(muxc->parent, client,
data->last_chan);
}
/* otherwise leave as-is */
return 0;
}
static ssize_t idle_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
}
static ssize_t idle_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
(val < 0 || val >= data->chip->nchans))
return -EINVAL;
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
WRITE_ONCE(data->idle_state, val);
/*
* Set the mux into a state consistent with the new
* idle_state.
*/
if (data->last_chan || val != MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(idle_state);
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
{
struct pca954x *data = dev_id;
unsigned long pending;
int ret, i;
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return IRQ_NONE;
pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1);
for_each_set_bit(i, &pending, data->chip->nchans)
handle_nested_irq(irq_linear_revmap(data->irq, i));
return IRQ_RETVAL(pending);
}
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
{
if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW)
return -EINVAL;
return 0;
}
static struct irq_chip pca954x_irq_chip = {
.name = "i2c-mux-pca954x",
.irq_set_type = pca954x_irq_set_type,
};
static int pca954x_irq_setup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
int c, irq;
if (!data->chip->has_irq || client->irq <= 0)
return 0;
raw_spin_lock_init(&data->lock);
data->irq = irq_domain_add_linear(client->dev.of_node,
data->chip->nchans,
&irq_domain_simple_ops, data);
if (!data->irq)
return -ENODEV;
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_create_mapping(data->irq, c);
if (!irq) {
dev_err(&client->dev, "failed irq create map\n");
return -EINVAL;
}
irq_set_chip_data(irq, data);
irq_set_chip_and_handler(irq, &pca954x_irq_chip,
handle_simple_irq);
}
return 0;
}
static void pca954x_cleanup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
int c, irq;
if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c);
irq_dispose_mapping(irq);
}
irq_domain_remove(data->irq);
}
i2c_mux_del_adapters(muxc);
}
static int pca954x_init(struct i2c_client *client, struct pca954x *data)
{
int ret;
if (data->idle_state >= 0)
data->last_chan = pca954x_regval(data, data->idle_state);
else
data->last_chan = 0; /* Disconnect multiplexer */
ret = i2c_smbus_write_byte(client, data->last_chan);
if (ret < 0)
data->last_chan = 0;
return ret;
}
/*
* I2C init/probing/exit functions
*/
static int pca954x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adap = client->adapter;
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct gpio_desc *gpio;
int num, force, class;
struct i2c_mux_core *muxc;
struct pca954x *data;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
pca954x_select_chan, pca954x_deselect_mux);
if (!muxc)
return -ENOMEM;
data = i2c_mux_priv(muxc);
i2c_set_clientdata(client, muxc);
data->client = client;
/* Reset the mux if a reset GPIO is specified. */
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
udelay(1);
gpiod_set_value_cansleep(gpio, 0);
/* Give the chip some time to recover. */
udelay(1);
}
data->chip = device_get_match_data(dev);
if (!data->chip)
data->chip = &chips[id->driver_data];
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
struct i2c_device_identity id;
ret = i2c_get_device_id(client, &id);
if (ret && ret != -EOPNOTSUPP)
return ret;
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
id.part_id != data->chip->id.part_id)) {
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
return -ENODEV;
}
}
data->idle_state = MUX_IDLE_AS_IS;
if (device_property_read_u32(dev, "idle-state", &data->idle_state)) {
if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
data->idle_state = MUX_IDLE_DISCONNECT;
}
/*
* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to a channel
* or disconnected state.
*/
ret = pca954x_init(client, data);
if (ret < 0) {
dev_warn(dev, "probe failed\n");
return -ENODEV;
}
ret = pca954x_irq_setup(muxc);
if (ret)
goto fail_cleanup;
/* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) {
force = 0; /* dynamic adap number */
class = 0; /* no class by default */
if (pdata) {
if (num < pdata->num_modes) {
/* force static number */
force = pdata->modes[num].adap_id;
class = pdata->modes[num].class;
} else
/* discard unconfigured channels */
break;
}
ret = i2c_mux_add_adapter(muxc, force, num, class);
if (ret)
goto fail_cleanup;
}
if (data->irq) {
ret = devm_request_threaded_irq(dev, data->client->irq,
NULL, pca954x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
"pca954x", data);
if (ret)
goto fail_cleanup;
}
/*
* The attr probably isn't going to be needed in most cases,
* so don't fail completely on error.
*/
device_create_file(dev, &dev_attr_idle_state);
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name);
return 0;
fail_cleanup:
pca954x_cleanup(muxc);
return ret;
}
static int pca954x_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
device_remove_file(&client->dev, &dev_attr_idle_state);
pca954x_cleanup(muxc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int pca954x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int ret;
ret = pca954x_init(client, data);
if (ret < 0)
dev_err(&client->dev, "failed to verify mux presence\n");
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "ctc_pca954x",
.pm = &pca954x_pm,
.of_match_table = pca954x_of_match,
},
.probe = pca954x_probe,
.remove = pca954x_remove,
.id_table = pca954x_id,
};
module_i2c_driver(pca954x_driver);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,48 @@
/*
*
* pca954x.h - I2C multiplexer/switch support
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@xxxxxxxx>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@xxxxxxxxxxx>
* Michael Lawnick <michael.lawnick.ext@xxxxxxx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _LINUX_I2C_PCA954X_H
#define _LINUX_I2C_PCA954X_H
/* Platform data for the PCA954x I2C multiplexers */
/* Per channel initialisation data:
* @adap_id: bus number for the adapter. 0 = don't care
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
* of this channel after transaction.
*
*/
struct pca954x_platform_mode {
int adap_id;
unsigned int deselect_on_exit:1;
unsigned int class;
};
/* Per mux/switch data, used with i2c_register_board_info */
struct pca954x_platform_data {
struct pca954x_platform_mode *modes;
int num_modes;
};
#endif /* _LINUX_I2C_PCA954X_H */

Просмотреть файл

@ -12,7 +12,7 @@
images {
kernel_ctc {
description = "ARM64 Kernel";
data = /incbin/("./vmlinuz-4.19.0-12-2-arm64");
data = /incbin/("./vmlinuz-5.10.0-8-2-arm64");
type = "kernel";
arch = "arm64";
os = "linux";
@ -25,7 +25,7 @@
};
initramfs {
description = "initramfs";
data = /incbin/("./initrd.img-4.19.0-12-2-arm64");
data = /incbin/("./initrd.img-5.10.0-8-2-arm64");
type = "ramdisk";
arch = "arm64";
os = "linux";

Просмотреть файл

@ -7,5 +7,5 @@ Standards-Version: 3.9.3
Package: tsingma-bsp
Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned
Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for tsingma bsp

2
platform/centec-arm64/tsingma-bsp/debian/rules Normal file → Executable file
Просмотреть файл

@ -15,7 +15,7 @@ PACKAGE_PRE_NAME := tsingma-bsp
KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd)
MODULE_DIRS:= ctc5236-mc ctc5236_switch ctcmac ctc_wdt ehci-ctc gpio-ctc i2c-ctc pinctrl-ctc pwm-ctc rtc-sd2405 sdhci-ctc5236 spi-ctc-qspi
MODULE_DIRS:= ctc5236-mc ctc5236_switch ctcmac ctc_wdt ehci-ctc gpio-ctc i2c-ctc pinctrl-ctc pwm-ctc rtc-sd2405 sdhci-ctc5236 spi-ctc-qspi ctc-phy
DTS_DIR := ctc-dts
MODULE_DIR := src
UTILS_DIR := utils

Просмотреть файл

@ -1,17 +1,17 @@
src/ctc5236-mc/ctc5236-mc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/pwm-ctc/pwm-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ctc5236_switch/ctc5236_switch.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/pinctrl-ctc/pinctrl-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ctc_wdt/ctc_wdt.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ctcmac/ctcmac.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ctcmac/ctcmac_test.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ctcmac/ctc5236_mdio.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/i2c-ctc/i2c-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/gpio-ctc/gpio-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ehci-ctc/ehci-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/rtc-sd2405/rtc-sd2405.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/sdhci-ctc5236/sdhci-ctc5236.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/spi-ctc-qspi/spi-ctc-qspi.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
src/ctc5236-mc/ctc5236-mc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/pwm-ctc/pwm-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc5236_switch/ctc5236_switch.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/pinctrl-ctc/pinctrl-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc_wdt/ctc_wdt.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctcmac/ctcmac.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctcmac/ctc5236_mdio.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc-phy/mars.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/i2c-ctc/i2c-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/gpio-ctc/gpio-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ehci-ctc/ehci-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/rtc-sd2405/rtc-sd2405.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/sdhci-ctc5236/sdhci-ctc5236.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/spi-ctc-qspi/spi-ctc-qspi.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc-dts/e530-ctc5236.dtb /boot/
src/config/fw_env.config /etc/
src/config/tsingma-bsp.service /lib/systemd/system

Просмотреть файл

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/*
* This header provides constants for the ARM GIC.
*/

Просмотреть файл

@ -88,7 +88,11 @@
memory-controller@30600000 {
compatible = "ctc,ctc5236-ddr-ctrl";
reg = <0x0 0x30600000 0x0 0x100000>;
interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 100 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 101 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
ctc,sysctrl = <&sysctrl>;
};
sysctrl: sysctrl@33200000 {
@ -136,12 +140,15 @@
#size-cells = <2>;
interrupt-parent = <&gic>;
status = "disabled";
local-mac-address = [00 00 00 00 00 00];
index = <0x00>;
reg = <0x0 0x33410000 0x0 0x10000>,
<0x0 0x33400000 0x0 0x10000>;
interrupts = <GIC_SPI 40 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 41 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
<GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 136 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 102 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
ctc,sysctrl = <&sysctrl>;
};
@ -152,12 +159,15 @@
#size-cells = <2>;
interrupt-parent = <&gic>;
status = "disabled";
local-mac-address = [00 00 00 00 00 00];
index = <0x01>;
reg = <0x0 0x33420000 0x0 0x10000>,
<0x0 0x33400000 0x0 0x10000>;
interrupts = <GIC_SPI 42 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 43 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
<GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 137 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 103 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
ctc,sysctrl = <&sysctrl>;
};
@ -165,6 +175,7 @@
compatible = "ctc-ehci";
reg = <0x0 0x30500000 0x0 0x1000>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
ctc,sysctrl = <&sysctrl>;
status = "disabled";
};
@ -172,7 +183,7 @@
compatible = "generic-ohci";
reg = <0x0 0x30580000 0x0 0x1000>;
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
status = "okay";
};
spi: spi@33100000 {
@ -188,7 +199,7 @@
status ="disabled";
};
qspi: qspi@10000000 {
qspi: spi@10000000 {
compatible = "ctc, igdaxi001a-qspi";
#address-cells = <1>;
#size-cells = <0>;
@ -228,6 +239,8 @@
reg = <0x0 0x33700000 0x0 0x1000>;
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2c_clk>;
ctc,sysctrl = <&sysctrl>;
i2c-num = <0>;
status ="disabled";
};
@ -238,6 +251,8 @@
reg = <0x0 0x33701000 0x0 0x1000>;
interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2c_clk>;
ctc,sysctrl = <&sysctrl>;
i2c-num = <1>;
status ="disabled";
};
@ -257,7 +272,8 @@
interrupt-names = "msi","aer","pme";
msi-parent = <&pcie>;
bus-range = <0 0xff>;
ranges = <0x43000000 0 0x00000000 0 0x40000000 0 0x40000000>;
ranges = <0x42000000 0 0x00000000 0 0x40000000 0 0x20000000
0x02000000 0 0x20000000 0 0x60000000 0 0x20000000>;
num-lanes = <1>;
ctc,sysctrl = <&sysctrl>;
status ="disabled";
@ -338,6 +354,7 @@
reg = <0x0 0x33610000 0x0 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
ctc,sysctrl = <&sysctrl>;
porta: gpio-port@0 {
compatible = "ctc,apb-gpio-porta";
@ -368,7 +385,42 @@
ctc,pinctrl-bank0 = <16>;
ctc,pinctrl-bank1 = <8>;
ctc,sysctrl = <&sysctrl>;
status = "okay";
spi0 {
spi_pin: spi_pin {
ctc,pins = <0 0 PIN_FUNC_SPI>,
<0 2 PIN_FUNC_SPI>,
<0 3 PIN_FUNC_SPI>,
<0 4 PIN_FUNC_SPI>,
<0 5 PIN_FUNC_SPI>,
<0 6 PIN_FUNC_SPI>,
<0 7 PIN_FUNC_SPI>;
};
};
uart2 {
uart2_pin: uart2_pin {
ctc,pins = <0 10 PIN_FUNC_UART>,
<0 11 PIN_FUNC_UART>,
<0 12 PIN_FUNC_UART>,
<0 13 PIN_FUNC_UART>,
<0 14 PIN_FUNC_UART>,
<0 15 PIN_FUNC_UART>;
};
};
fc {
fc_pin: fc_pin {
ctc,pins = <1 0 PIN_FUNC_FC>,
<1 1 PIN_FUNC_FC>,
<1 2 PIN_FUNC_FC>,
<1 3 PIN_FUNC_FC>,
<1 4 PIN_FUNC_FC>,
<1 5 PIN_FUNC_FC>,
<1 6 PIN_FUNC_FC>,
<1 7 PIN_FUNC_FC>;
};
};
};
};

Просмотреть файл

@ -157,6 +157,7 @@
non-removable;
no-sd;
no-sdio;
cap-mmc-hw-reset;
voltage-ranges = <3300 3300>;
status = "okay";
};
@ -187,7 +188,7 @@
&pinctrl {
spi {
spi0 {
spi_pin: spi_pin {
status = "disabled";
ctc,pins = <0 0 PIN_FUNC_SPI>,

Просмотреть файл

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/*
* This header provides constants for most IRQ bindings.
*

Просмотреть файл

@ -0,0 +1 @@
obj-m = mars.o

Просмотреть файл

@ -0,0 +1,316 @@
/*
* drivers/net/phy/mars.c
*
* Driver for Centec PHYs
*
* Author: liuht
*
* Copyright 2002-2018, Centec Networks (Suzhou) Co., Ltd.
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/of.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
/* Mask used for ID comparisons */
#define CTC_PHY_ID_MASK 0xffffffff
/* Known PHY IDs */
#define CTC_PHY_ID_MARS1S_V1 0x00782013
#define CTC_PHY_ID_MARS1S 0x01E04013
#define CTC_PHY_ID_MARS1P_V1 0x00782011
#define CTC_PHY_ID_MARS1P 0x01E04011
#define CTC_PHY_IMASK 0x12
#define CTC_PHY_IEVENT 0x13
#define CTC_PHY_IMASK_INIT 0x6c00
#define CTC_PHY_IMASK_CLEAR 0x0000
#define CTC_PHY_REG_SPACE 0
#define CTC_SDS_REG_SPACE 1
static int mars_ext_read(struct phy_device *phydev, u32 regnum)
{
int ret;
ret = phy_write(phydev, 0x1e, regnum);
if (ret < 0)
return ret;
return phy_read(phydev, 0x1f);
}
static int mars_ext_write(struct phy_device *phydev, u32 regnum, u16 val)
{
int ret;
ret = phy_write(phydev, 0x1e, regnum);
if (ret < 0)
return ret;
return phy_write(phydev, 0x1f, val);
}
static int mars_select_reg_space(struct phy_device *phydev, int space)
{
int ret;
if (space == CTC_PHY_REG_SPACE) {
ret = mars_ext_write(phydev, 0xa000, 0x0);
} else {
ret = mars_ext_write(phydev, 0xa000, 0x2);
}
return ret;
}
static int mars_config_advert(struct phy_device *phydev)
{
int err, bmsr, changed = 0;
u32 adv;
/* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
/* Setup standard advertisement */
err = phy_modify_changed(phydev, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_100BASE4 |
ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
adv);
if (err < 0)
return err;
if (err > 0)
changed = 1;
bmsr = phy_read(phydev, MII_BMSR);
if (bmsr < 0)
return bmsr;
/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
* 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
* logical 1.
*/
if (!(bmsr & BMSR_ESTATEN))
return changed;
adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
err = phy_modify_changed(phydev, MII_CTRL1000,
ADVERTISE_1000FULL | ADVERTISE_1000HALF, adv);
if (err < 0)
return err;
if (err > 0)
changed = 1;
return changed;
}
int mars1s_config_aneg(struct phy_device *phydev)
{
int err, changed = 0;
if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);
err = mars_config_advert(phydev);
if (err < 0) /* error */
return err;
changed |= err;
if (changed == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
int ctl = phy_read(phydev, MII_BMCR);
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
changed = 1; /* do restart aneg */
}
/* Only restart aneg if we are advertising something different
* than we were before.
*/
if (changed > 0)
return genphy_restart_aneg(phydev);
return 0;
}
static int mars_ack_interrupt(struct phy_device *phydev)
{
int err;
#if 1
/* Clear the interrupts by reading the reg */
err = phy_read(phydev, CTC_PHY_IEVENT);
#else
err = mars_ext_read(phydev, 0xa011);
#endif
if (err < 0)
return err;
return 0;
}
static int mars_config_intr(struct phy_device *phydev)
{
int err;
#if 1
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, CTC_PHY_IMASK, CTC_PHY_IMASK_INIT);
else
err = phy_write(phydev, CTC_PHY_IMASK, CTC_PHY_IMASK_CLEAR);
#else
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = mars_ext_write(phydev, 0xa010, 0xffff);
else
err = mars_ext_write(phydev, 0xa010, 0x0000);
#endif
return err;
}
#if 0
static int mars_set_link_timer_6_3ms(struct phy_device *phydev)
{
int ret = 0;
ret = mars_select_reg_space(phydev, CTC_SDS_REG_SPACE);
if (!ret)
mars_ext_write(phydev, 0xa5, 0xc);
mars_select_reg_space(phydev, CTC_PHY_REG_SPACE);
return 0;
}
#endif
static int mars_set_link_timer_2_6ms(struct phy_device *phydev)
{
int ret = 0;
ret = mars_select_reg_space(phydev, CTC_SDS_REG_SPACE);
if (!ret)
mars_ext_write(phydev, 0xa5, 0x5);
mars_select_reg_space(phydev, CTC_PHY_REG_SPACE);
return 0;
}
int mars_config_init(struct phy_device *phydev)
{
return mars_set_link_timer_2_6ms(phydev);
}
int mars1p_config_init(struct phy_device *phydev)
{
/*RGMII clock 2.5M when link down, bit12:1->0 */
mars_ext_write(phydev, 0xc, 0x8051);
/*Disable sleep mode, bit15:1->0 */
mars_ext_write(phydev, 0x27, 0x2029);
/* disable PHY to respond to MDIO access with PHYAD0 */
/* MMD7 8001h: bit6: 0, change value: 0x7f --> 0x3f */
phy_write(phydev, 0xd, 0x7);
phy_write(phydev, 0xe, 0x8001);
phy_write(phydev, 0xd, 0x4007);
phy_write(phydev, 0xe, 0x3f);
return mars_set_link_timer_2_6ms(phydev);
}
static struct phy_driver ctc_drivers[] = {
{
.phy_id = CTC_PHY_ID_MARS1S,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1S",
.config_init = mars_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
{
.phy_id = CTC_PHY_ID_MARS1S_V1,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1S_V1",
.config_init = mars_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
{
.phy_id = CTC_PHY_ID_MARS1P,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1P",
.config_init = mars1p_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
{
.phy_id = CTC_PHY_ID_MARS1P_V1,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1P_V1",
.config_init = mars1p_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
module_phy_driver(ctc_drivers);
static struct mdio_device_id __maybe_unused mars_tbl[] = {
{CTC_PHY_ID_MARS1S, CTC_PHY_ID_MASK},
{CTC_PHY_ID_MARS1S_V1, CTC_PHY_ID_MASK},
{CTC_PHY_ID_MARS1P, CTC_PHY_ID_MASK},
{CTC_PHY_ID_MARS1P_V1, CTC_PHY_ID_MASK},
{}
};
MODULE_DEVICE_TABLE(mdio, mars_tbl);

Просмотреть файл

@ -1,4 +1,5 @@
/* Centec TsingMa Memory Controller Driver
/*
* Centec TsingMa Memory Contoller Driver
*
* Author: lius <lius@centecnetworks.com>
*
@ -22,22 +23,29 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include "../include/sysctl.h"
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
struct ctc5236_mc {
struct device *dev;
void __iomem *base;
int irq;
int irq1; /* one bit ecc error irq num, only use in TM1.1 */
int irq2; /* more than one bit ecc error irq num, only use in TM1.1 */
int irq_cache_ecc; /* cache error interrupt */
struct regmap *regmap_base;
unsigned int soc_ver;
};
/* DDR interrupt enable register */
#define DDR_ERR_INT_EN 0xF0
#define DDR_ERR_INT_EN 0xF0
/* DDR interrupt status register */
#define DDR_ERR_INT_STATUS 0xF4
#define DDR_ERR_INT_STATUS 0xF4
/* over top-bound info register*/
#define DDR_ERR_INT_OVER_TOPBOUND_L 0xF8
#define DDR_ERR_INT_OVER_TOPBOUND_L 0xF8
#define DDR_ERR_INT_OVER_TOPBOUND_H 0xFC
#define DDR_PORT0_ERR_INT_STATUS 0x1
@ -45,15 +53,16 @@ struct ctc5236_mc {
#define DDR_PORT2_ERR_INT_STATUS 0x3
#define DDR_PORT3_ERR_INT_STATUS 0x4
#define DDR_ERR_ECC_INT_STATUS 0x10000
#define DDR_ERR_CRC_INT_STATUS 0x200000
#define DDR_ERR_WR_PORT_REC_UNDERFLOW 0x20000
#define DDR_ERR_WR_PORT_REC_OVERFLOW 0x40000
#define DDR_ERR_RD_PORT_REC_UNDERFLOW 0x80000
#define DDR_ERR_RD_PORT_REC_OVERFLOW 0x100000
#define DDR_PORT0_STATUS 0xB0
#define DDR_PORT1_STATUS 0xB4
#define DDR_PORT2_STATUS 0xB8
#define DDR_PORT3_STATUS 0xBC
#define DDR_PORT0_STATUS 0xB0
#define DDR_PORT1_STATUS 0xB4
#define DDR_PORT2_STATUS 0xB8
#define DDR_PORT3_STATUS 0xBC
#define DDR_ERR_OVER_TOPBOUND 0x20000
#define DDR_ERR_WCMDQ_OVER 0x40000
@ -76,10 +85,10 @@ struct ctc5236_mc {
#define DDR_PORT2_BASE 0xB8
#define DDR_PORT3_BASE 0xBc
#define DDR_PORT0 0
#define DDR_PORT1 1
#define DDR_PORT2 2
#define DDR_PORT3 3
#define DDR_PORT0 0
#define DDR_PORT1 1
#define DDR_PORT2 2
#define DDR_PORT3 3
static int port_err_status(int status, int port, void *dev_id)
{
@ -98,53 +107,80 @@ static int port_err_status(int status, int port, void *dev_id)
temp = (addr_l | (((unsigned long)((addr_h >> 12) & 0x3)) << 32)
);
pr_emerg("ERROR:port%d is out of top-bound range!\n"
"The error address is 0x%p\n",
id, (void *)temp);
printk(KERN_EMERG
"ERROR:port%d is out of top-bound range!\n The error address is 0x%p\n",
id, (void *)temp);
}
if (status & DDR_ERR_WCMDQ_OVER)
pr_err("ERROR:port%d write command queue is overflow!\n", id);
if (status & DDR_ERR_WCMDQ_OVER) {
printk(KERN_ERR
"ERROR:port%d write command queue is overflow!\n", id);
}
if (status & DDR_ERR_WCMDQ_UNDER)
pr_err("ERROR:port%d write command queue is underflow!\n", id);
if (status & DDR_ERR_WCMDQ_UNDER) {
printk(KERN_ERR
"ERROR:port%d write command queue is underflow!\n", id);
}
if (status & DDR_ERR_WDATAQ_OVER)
pr_err("ERROR:port%d write data queue is overflow!\n", id);
if (status & DDR_ERR_WDATAQ_OVER) {
printk(KERN_ERR "ERROR:port%d write data queue is overflow!\n",
id);
}
if (status & DDR_ERR_WDATAQ_UNDER)
pr_err("ERROR:port%d write data queue is underflow!\n", id);
if (status & DDR_ERR_WDATAQ_UNDER) {
printk(KERN_ERR "ERROR:port%d write data queue is underflow!\n",
id);
}
if (status & DDR_ERR_WESPQ_OVER)
pr_err("ERROR:port%d write response queue is overflow!\n", id);
if (status & DDR_ERR_WESPQ_OVER) {
printk(KERN_ERR
"ERROR:port%d write response queue is overflow!\n", id);
}
if (status & DDR_ERR_WESPQ_UNDER)
pr_err("ERROR:port%d write response queue is underflow!\n", id);
if (status & DDR_ERR_WESPQ_UNDER) {
printk(KERN_ERR
"ERROR:port%d write response queue is underflow!\n", id);
}
if (status & DDR_ERR_WINFOQ_OVER)
pr_err("ERROR:port%d write info queue is overflow!\n", id);
if (status & DDR_ERR_WINFOQ_OVER) {
printk(KERN_ERR "ERROR:port%d write info queue is overflow!\n",
id);
}
if (status & DDR_ERR_WINFOQ_UNDER)
pr_err("ERROR:port%d write info queue is underflow!\n", id);
if (status & DDR_ERR_WINFOQ_UNDER) {
printk(KERN_ERR "ERROR:port%d write info queue is underflow!\n",
id);
}
if (status & DDR_ERR_RCMDQ_OVER)
pr_err("ERROR:port%d read command queue is overflow!\n", id);
if (status & DDR_ERR_RCMDQ_OVER) {
printk(KERN_ERR
"ERROR:port%d read command queue is overflow!\n", id);
}
if (status & DDR_ERR_RCMDQ_UNDER)
pr_err("ERROR:port%d read command queue is underflow!\n", id);
if (status & DDR_ERR_RCMDQ_UNDER) {
printk(KERN_ERR
"ERROR:port%d read command queue is underflow!\n", id);
}
if (status & DDR_ERR_RDATAQ_OVER)
pr_err("ERROR:port%d read data queue is overflow!\n", id);
if (status & DDR_ERR_RDATAQ_OVER) {
printk(KERN_ERR "ERROR:port%d read data queue is overflow!\n",
id);
}
if (status & DDR_ERR_RDATAQ_UNDER)
pr_err("ERROR:port%d read data queue is underflow!\n", id);
if (status & DDR_ERR_RDATAQ_UNDER) {
printk(KERN_ERR "ERROR:port%d read data queue is underflow!\n",
id);
}
if (status & DDR_ERR_RESPQ_OVER)
pr_err("ERROR:port%d read response queue is overflow!\n", id);
if (status & DDR_ERR_RESPQ_UNDER)
pr_err("ERROR:port%d read response queue is underflow!\n", id);
if (status & DDR_ERR_RESPQ_OVER) {
printk(KERN_ERR
"ERROR:port%d read response queue is overflow!\n", id);
}
if (status & DDR_ERR_RESPQ_UNDER) {
printk(KERN_ERR
"ERROR:port%d read response queue is underflow!\n", id);
}
return 1;
}
@ -177,20 +213,29 @@ static irqreturn_t ctc_mc_err_handler(int irq, void *dev_id)
port_err_status(ret, DDR_PORT3, mci);
}
if (status & DDR_ERR_ECC_INT_STATUS)
pr_err("ERROR:The ecc more than 1-bit error !\n");
if (status & DDR_ERR_ECC_INT_STATUS) {
printk(KERN_ERR "ERROR:The ecc more than 1-bit error !\n");
}
if (status & DDR_ERR_WR_PORT_REC_UNDERFLOW)
pr_err("ERROR:MPARB wr_port_rec FIFO is underflow!\n");
if (status & DDR_ERR_WR_PORT_REC_UNDERFLOW) {
printk(KERN_ERR "ERROR:MPARB wr_port_rec FIFO is underflow!\n");
}
if (status & DDR_ERR_WR_PORT_REC_OVERFLOW)
pr_err("ERROR:MPARB wr_port_rec FIFO is overflow!\n");
if (status & DDR_ERR_WR_PORT_REC_OVERFLOW) {
printk(KERN_ERR "ERROR:MPARB wr_port_rec FIFO is overflow!\n");
}
if (status & DDR_ERR_RD_PORT_REC_UNDERFLOW)
pr_err("ERROR:MPARB rd_port_rec FIFO is underflow!\n");
if (status & DDR_ERR_RD_PORT_REC_UNDERFLOW) {
printk(KERN_ERR "ERROR:MPARB rd_port_rec FIFO is underflow!\n");
}
if (status & DDR_ERR_RD_PORT_REC_OVERFLOW)
pr_err("ERROR:MPARB rd_port_rec FIFO is underflow!\n");
if (status & DDR_ERR_RD_PORT_REC_OVERFLOW) {
printk(KERN_ERR "ERROR:MPARB rd_port_rec FIFO is overflow!\n");
}
if (status & DDR_ERR_CRC_INT_STATUS) {
printk(KERN_ERR "ERROR:The crc error from DRAM!\n");
}
/* disable DDR interrupt */
writel(0x0, mci->base + DDR_ERR_INT_EN);
@ -198,6 +243,43 @@ static irqreturn_t ctc_mc_err_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static irqreturn_t ctc_mc_onebit_ecc_err_handler(int irq, void *dev_id)
{
struct ctc5236_mc *mci = dev_id;
unsigned int val;
printk(KERN_ERR "ERROR:One-Bit ECC Error!\n");
regmap_read(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), &val);
printk(KERN_ERR "One-Bit ECC Error Count is %d\n", ((val >> 8) & 0xf));
printk(KERN_ERR "more than One-Bit ECC Error Count is %d\n",
((val >> 12) & 0xf));
return IRQ_HANDLED;
}
static irqreturn_t ctc_mc_twobit_ecc_err_handler(int irq, void *dev_id)
{
struct ctc5236_mc *mci = dev_id;
unsigned int val;
printk(KERN_ERR "ERROR:more than One-Bit ECC Error!\n");
regmap_read(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), &val);
printk(KERN_ERR "One-Bit ECC Error Count is %d\n", ((val >> 8) & 0xf));
printk(KERN_ERR "more than One-Bit ECC Error Count is %d\n",
((val >> 12) & 0xf));
return IRQ_HANDLED;
}
static irqreturn_t ctc_cache_err_handler(int irq, void *dev_id)
{
printk(KERN_ERR "ERROR:Cache ECC Error!\n");
return IRQ_HANDLED;
}
static const struct of_device_id ctc5236_ddr_ctrl_of_match[] = {
{
.compatible = "ctc,ctc5236-ddr-ctrl",
@ -212,6 +294,7 @@ static int ctc5236_mc_probe(struct platform_device *pdev)
const struct of_device_id *id;
struct ctc5236_mc *mci;
int ret;
unsigned int val;
id = of_match_device(ctc5236_ddr_ctrl_of_match, &pdev->dev);
if (!id)
@ -225,12 +308,67 @@ static int ctc5236_mc_probe(struct platform_device *pdev)
if (IS_ERR(mci->base))
return PTR_ERR(mci->base);
mci->regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(mci->regmap_base))
return PTR_ERR(mci->regmap_base);
regmap_read(mci->regmap_base,
offsetof(struct SysCtl_regs, SysCtlSysRev), &val);
mci->soc_ver = val;
mci->irq = platform_get_irq(pdev, 0);
ret =
devm_request_irq(&pdev->dev, mci->irq, ctc_mc_err_handler, 0,
dev_name(&pdev->dev), mci);
"DDR Ecc", mci);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to request irq %d\n", mci->irq);
dev_err(&pdev->dev, "Unable to request ddr error irq %d\n",
mci->irq);
goto err;
}
val = readl(mci->base);
/* register ecc interrupt when use TM1.1 soc and enable ecc function */
if ((0x0 != (val & BIT(10))) && (0x1 == mci->soc_ver)) {
/* clean ecc status */
regmap_write(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), 0x1);
regmap_write(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), 0x0);
mci->irq1 = platform_get_irq(pdev, 1);
ret =
devm_request_irq(&pdev->dev, mci->irq1,
ctc_mc_twobit_ecc_err_handler, 0,
"DDR two-bit Ecc(TM1.1)", mci);
if (ret < 0) {
dev_err(&pdev->dev,
"Unable to request ddr two-bit ecc error irq %d\n",
mci->irq1);
goto err;
}
mci->irq2 = platform_get_irq(pdev, 2);
ret =
devm_request_irq(&pdev->dev, mci->irq2,
ctc_mc_onebit_ecc_err_handler, 0,
"DDR one-bit Ecc(TM1.1)", mci);
if (ret < 0) {
dev_err(&pdev->dev,
"Unable to request one-bit ecc error irq %d\n",
mci->irq2);
goto err;
}
}
mci->irq_cache_ecc = platform_get_irq(pdev, 3);
ret =
devm_request_irq(&pdev->dev, mci->irq_cache_ecc,
ctc_cache_err_handler, 0, "Cache Ecc", mci);
if (ret < 0) {
dev_err(&pdev->dev,
"Unable to request cache ecc error irq %d\n",
mci->irq_cache_ecc);
goto err;
}
@ -276,4 +414,4 @@ module_exit(ctc5236_mc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Centec Network");
MODULE_DESCRIPTION("Centec TsingMa memory controller driver");
MODULE_DESCRIPTION("Centec TsingMa memory contoller driver");

Просмотреть файл

@ -1,4 +1,5 @@
/* (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD.
/*
* (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD.
* Wangyb <wangyb@centecnetworks.com>
*
* SPDX-License-Identifier: GPL-2.0+
@ -9,15 +10,15 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <../include/ctc5236_switch.h>
#include "../include/ctc5236_switch.h"
struct ctc_access_t *access;
ctc_access_t *access;
#define SWITCH_DTS_OFFSET 0x1000
#define SWITCH_DTS_OFFSET 0x1000
int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
int ctc5236_switch_read(u32 offset, u32 len, u32 * p_value)
{
union ctc_switch_cmd_status_u_t cmd_status_u;
ctc_switch_cmd_status_u_t cmd_status_u;
u32 timeout = 0x6400;
u32 cmd_len = 0;
u8 index = 0;
@ -28,8 +29,8 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
}
/* switch only have 16 databuf, len must not exceed 16 */
if (len > 16 || len == 0) {
pr_err("switch read: length error! len = %d\n", len);
if ((16 < len) || (0 == len)) {
pr_err("switch read: length error! len = %d \n", len);
return -1;
}
/* cmdDataLen must be power of 2 */
@ -39,22 +40,23 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
cmd_len = len;
do {
cmd_len++;
} while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
}
while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
}
/* 1. write CmdStatusReg */
memset(&cmd_status_u, 0, sizeof(union ctc_switch_cmd_status_u_t));
memset(&cmd_status_u, 0, sizeof(ctc_switch_cmd_status_u_t));
cmd_status_u.cmd_status.cmdReadType = 1;
/* normal operate only support 1 entry */
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len;
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len; /* normal operate only support 1 entry */
cmd_status_u.cmd_status.cmdDataLen = len;
writel(cmd_status_u.val, &access->cmd_status);
/* 2. write AddrReg */
writel(offset, &access->addr);
/* 3. polling status and check */
cmd_status_u.val = readl(&access->cmd_status);
while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout))
while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout)) {
cmd_status_u.val = readl(&access->cmd_status);
}
/* 4. check cmd done */
if (!(cmd_status_u.cmd_status.reqProcDone)) {
pr_err("switch read error! cmd_status = %x\n",
@ -69,15 +71,16 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
}
/* 6. read data from buffer */
for (index = 0; index < len; index++)
for (index = 0; index < len; index++) {
p_value[index] = readl(&access->data[index]);
}
return 0;
}
int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
int ctc5236_switch_write(u32 offset, u32 len, u32 * p_value)
{
union ctc_switch_cmd_status_u_t cmd_status_u;
ctc_switch_cmd_status_u_t cmd_status_u;
u32 timeout = 0x6400; /* need to be confirmed */
u32 cmd_len = 0;
u8 index = 0;
@ -88,8 +91,8 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
}
/* switch only have 16 databuf, len must not exceed 16 */
if (len > 16 || len == 0) {
pr_err("switch write length error! len = %d\n", len);
if ((16 < len) || (0 == len)) {
pr_err("switch write length error! len = %d \n", len);
return -1;
}
@ -100,28 +103,28 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
cmd_len = len;
do {
cmd_len++;
} while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
}
while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
}
/* 1. write CmdStatusReg */
memset(&cmd_status_u, 0, sizeof(struct ctc_switch_cmd_status_t));
memset(&cmd_status_u, 0, sizeof(ctc_switch_cmd_status_t));
cmd_status_u.cmd_status.cmdReadType = 0;
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len;
/* Notice: for 1 entry op, cmdDatalen eq cmdEntryWords,
* but for mutil entry, len = cmd_len
*/
cmd_status_u.cmd_status.cmdDataLen = len;
cmd_status_u.cmd_status.cmdDataLen = len; /* Notice: for 1 entry op, cmdDatalen eq cmdEntryWords, but for mutil entry, len = cmd_len */
writel(cmd_status_u.val, &access->cmd_status);
/* 2. write AddrReg */
writel(offset, &access->addr);
/* 3. write data into databuffer */
for (index = 0; index < len; index++)
for (index = 0; index < len; index++) {
writel(p_value[index], &access->data[index]);
}
/* 4. polling status and check */
cmd_status_u.val = readl(&access->cmd_status);
while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout))
while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout)) {
cmd_status_u.val = readl(&access->cmd_status);
}
/* 5. check cmd done */
if (!(cmd_status_u.cmd_status.reqProcDone)) {
@ -141,29 +144,29 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
}
static int
_sys_tsingma_peri_get_temp_with_code(u8 lchip, u32 temp_code, u32 *p_temp_val)
_sys_tsingma_peri_get_temp_with_code(u8 lchip, u32 temp_code, u32 * p_temp_val)
{
u16 temp_mapping_tbl[SYS_TSINGMA_TEMP_TABLE_NUM + 1] = {
804, 801, 798, 795, 792, 790, 787, 784, 781, 778,
775, 772, 769, 766, 763, 761, 758, 755, 752, 749,
746, 743, 740, 737, 734, 731, 728, 725, 722, 719,
717, 714, 711, 708, 705, 702, 699, 696, 693, 690,
687, 684, 681, 678, 675, 672, 669, 666, 663, 660,
658, 655, 652, 649, 646, 643, 640, 637, 634, 631,
628, 625, 622, 619, 616, 613, 610, 607, 604, 601,
599, 596, 593, 590, 587, 584, 581, 578, 575, 572,
569, 566, 563, 560, 557, 554, 551, 548, 545, 542,
540, 537, 534, 531, 528, 525, 522, 519, 516, 513,
510, 507, 504, 501, 498, 495, 492, 489, 486, 483,
481, 478, 475, 472, 469, 466, 463, 460, 457, 454,
451, 448, 445, 442, 439, 436, 433, 430, 427, 424,
421, 418, 415, 412, 409, 406, 403, 400, 397, 394,
391, 388, 385, 382, 379, 376, 373, 370, 367, 364,
361, 358, 355, 352, 349, 346, 343, 340, 337, 334,
804, 801, 798, 795, 792, 790, 787, 784, 781, 778, 775, 772, 769, 766, 763, 761, 758, 755, 752, 749,
/*-40~-21*/
746, 743, 740, 737, 734, 731, 728, 725, 722, 719, 717, 714, 711, 708, 705, 702, 699, 696, 693, 690,
/*-20~-1*/
687, 684, 681, 678, 675, 672, 669, 666, 663, 660, 658, 655, 652, 649, 646, 643, 640, 637, 634, 631, /*0~19 */
628, 625, 622, 619, 616, 613, 610, 607, 604, 601, 599, 596, 593, 590, 587, 584, 581, 578, 575, 572, /*20~39 */
569, 566, 563, 560, 557, 554, 551, 548, 545, 542, 540, 537, 534, 531, 528, 525, 522, 519, 516, 513, /*40~59 */
510, 507, 504, 501, 498, 495, 492, 489, 486, 483, 481, 478, 475, 472, 469, 466, 463, 460, 457, 454, /*60~79 */
451, 448, 445, 442, 439, 436, 433, 430, 427, 424, 421, 418, 415, 412, 409, 406, 403, 400, 397, 394, /*80~99 */
391, 388, 385, 382, 379, 376, 373, 370, 367, 364, 361, 358, 355, 352, 349, 346, 343, 340, 337, 334, /*100~119 */
331, 328, 325, 322, 319, 316, 0
};
}; /*120~125 */
u8 index = 0;
/*if ((temp_code > temp_mapping_tbl[0]) || (temp_code < temp_mapping_tbl[SYS_TSINGMA_TEMP_TABLE_NUM-1]))
{
SYS_PERI_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "temp code error %d\n", temp_code);
return CTC_E_HW_INVALID_INDEX;
} */
for (index = 0; index < SYS_TSINGMA_TEMP_TABLE_NUM; index++) {
if ((temp_code <= temp_mapping_tbl[index])
&& (temp_code > temp_mapping_tbl[index + 1])) {
@ -171,10 +174,11 @@ _sys_tsingma_peri_get_temp_with_code(u8 lchip, u32 temp_code, u32 *p_temp_val)
}
}
if (index < 39)
if (index < 40) {
*p_temp_val = 40 - index + (1 << 31);
else
} else {
*p_temp_val = index - 40;
}
return 0;
}
@ -189,27 +193,6 @@ int get_switch_temperature(void)
offset = 0xf * 4;
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
/*config RTHMC_RST=1 */
/*mask_write tbl-reg OmcMem 0x10 offset 0x0 0x00000010 0x00000010 */
offset = 0x10 * 4;
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
value |= BIT(4);
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
/*wait RTHMC_RST=1 */
/*read tbl-reg OmcMem 0x10 offset 0x0 */
timeout = SYS_TSINGMA_SENSOR_TIMEOUT;
offset = 0x10 * 4;
while (timeout) {
timeout--;
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
if ((BIT(4) & value) == 0)
break;
msleep(1);
}
if (timeout == 0)
return 0xffff;
/*config ENBIAS=1£¬ENVR=1£¬ENAD=1 */
/*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x02000007 0x03000007 */
offset = 0x11 * 4;
@ -237,6 +220,8 @@ int get_switch_temperature(void)
value |= BIT(0);
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
msleep(1);
/*mask_write tbl-reg OmcMem 0x10 offset 0x0 0x00000001 0x00000001 */
offset = 0x10 * 4;
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
@ -262,8 +247,9 @@ int get_switch_temperature(void)
msleep(1);
}
if (timeout == 0)
if (0 == timeout) {
return 0xffff;
}
/*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x00000006 0x00000006 */
offset = 0x11 * 4;
@ -284,6 +270,7 @@ int get_switch_temperature(void)
return -temperature;
}
EXPORT_SYMBOL_GPL(get_switch_temperature);
static int ctc_switch_probe(struct platform_device *pdev)
@ -291,14 +278,16 @@ static int ctc_switch_probe(struct platform_device *pdev)
struct resource *iomem;
void __iomem *ioaddr;
resource_size_t start;
uint val;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
start = iomem->start - 0x1000;
ioaddr = devm_ioremap(&pdev->dev, start, resource_size(iomem));
if (IS_ERR(ioaddr))
if (IS_ERR(ioaddr)) {
return -1;
access = (struct ctc_access_t *) ioaddr;
}
access = (ctc_access_t *) ioaddr;
return 0;
}

Просмотреть файл

@ -1,3 +1,4 @@
/*
/* drivers/char/watchdog/ctc-wdt.c
*
* Watchdog driver for CTC TSINGMA, based on ARM SP805 watchdog module
@ -290,6 +291,12 @@ static int ctc_wdt_probe(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(wdt->regmap_base))
return PTR_ERR(wdt->regmap_base);
/* reset wdt module */
regmap_write(wdt->regmap_base,
offsetof(struct SysCtl_regs, SysWdtResetCtl), 0x3);
regmap_write(wdt->regmap_base,
offsetof(struct SysCtl_regs, SysWdtResetCtl), 0x0);
/*
* TsingMa SoC wdt reference clock is obtained by clockSub frequency
* division,which is 500Mhz.So we need to set the frequency division

Просмотреть файл

@ -1,3 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/tsingma-bsp/src/ctc5236_switch/Module.symvers
obj-m = ctcmac.o ctcmac_test.o ctc5236_mdio.o
obj-m = ctcmac.o ctc5236_mdio.o

Просмотреть файл

@ -1,4 +1,5 @@
/* Centec cpu_mac Ethernet Driver -- cpu_mac controller implementation
/*
* Centec CpuMac Ethernet Driver -- CpuMac controller implementation
* Provides Bus interface for MIIM regs
*
* Author: liuht <liuht@centecnetworks.com>
@ -10,6 +11,7 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
@ -44,7 +46,7 @@
struct ctc_mdio_priv {
void __iomem *map;
struct mdio_soc_regs *mdio_reg;
struct MdioSoc_regs *mdio_reg;
};
static int ctc_mdio_write(struct mii_bus *bus, int mii_id, int reg, u16 value)
@ -57,14 +59,15 @@ static int ctc_mdio_write(struct mii_bus *bus, int mii_id, int reg, u16 value)
cmd = CTCMAC_MDIO_CMD_REGAD(reg) | CTCMAC_MDIO_CMD_PHYAD(mii_id)
| CTCMAC_MDIO_CMD_OPCODE(1) | CTCMAC_MDIO_CMD_DATA(value);
writel(cmd, &priv->mdio_reg->mdio_soc_cmd_0[0]);
writel(1, &priv->mdio_reg->mdio_soc_cmd_0[1]);
writel(cmd, &priv->mdio_reg->MdioSocCmd0[0]);
writel(1, &priv->mdio_reg->MdioSocCmd0[1]);
ret = readl_poll_timeout(&priv->mdio_reg->mdio_soc_status_0,
ret = readl_poll_timeout(&priv->mdio_reg->MdioSocStatus0,
tmp, tmp & CTCMAC_MDIO_STAT(1), 1000, 10000);
if (ret < 0)
if (ret < 0) {
return -1;
}
return 0;
}
@ -80,18 +83,18 @@ static int ctc_mdio_read(struct mii_bus *bus, int mii_id, int reg)
cmd = CTCMAC_MDIO_CMD_REGAD(reg) | CTCMAC_MDIO_CMD_PHYAD(mii_id)
| CTCMAC_MDIO_CMD_OPCODE(2);
writel(cmd, &priv->mdio_reg->mdio_soc_cmd_0[0]);
writel(1, &priv->mdio_reg->mdio_soc_cmd_0[1]);
writel(cmd, &priv->mdio_reg->MdioSocCmd0[0]);
writel(1, &priv->mdio_reg->MdioSocCmd0[1]);
ret = readl_poll_timeout(&priv->mdio_reg->mdio_soc_status_0,
ret = readl_poll_timeout(&priv->mdio_reg->MdioSocStatus0,
status, status & CTCMAC_MDIO_STAT(1), 1000,
10000);
if (ret < 0) {
pr_err("ctc_mdio_read1\n");
printk(KERN_ERR "ctc_mdio_read1\n");
return -1;
}
value = (readl(&priv->mdio_reg->mdio_soc_status_0) & 0xffff);
value = (readl(&priv->mdio_reg->MdioSocStatus0) & 0xffff);
return value;
}
@ -100,7 +103,7 @@ static int ctc_mdio_reset(struct mii_bus *bus)
{
struct ctc_mdio_priv *priv = (struct ctc_mdio_priv *)bus->priv;
writel(0x91f, &priv->mdio_reg->mdio_soc_cfg_0);
writel(0x91f, &priv->mdio_reg->MdioSocCfg0);
return 0;
}
@ -146,7 +149,7 @@ static int ctc_mdio_probe(struct platform_device *pdev)
pr_err("of iomap fail %d!\n", err);
goto error;
}
priv->mdio_reg = (struct mdio_soc_regs *)priv->map;
priv->mdio_reg = (struct MdioSoc_regs *)priv->map;
new_bus->parent = &pdev->dev;
platform_set_drvdata(pdev, new_bus);

2156
platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c Normal file → Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Centec cpu_mac Ethernet Driver -- cpu_mac controller implementation
* Centec CpuMac Ethernet Driver -- CpuMac controller implementation
* Provides Bus interface for MIIM regs
*
* Author: liuht <liuht@centecnetworks.com>
@ -17,7 +17,7 @@
#ifndef __CTCMAC_H
#define __CTCMAC_H
#define TX_TIMEOUT (5 * HZ)
#define TX_TIMEOUT (5*HZ)
#define CTCMAC_DEFAULT_MTU 1500
#define CTCMAC_MIN_PKT_LEN 64
@ -47,14 +47,18 @@
#define CTCMAC_TOKEN_PER_PKT 10
#define CTCMAC_TIMER_COMPENSATE 1
#define CTCMAC_NOR_RX1_R BIT(7)
#define CTCMAC_NOR_RX0_R BIT(6)
#define CTCMAC_NOR_RX1_D BIT(5)
#define CTCMAC_NOR_RX0_D BIT(4)
#define CTCMAC_NOR_TX_D BIT(3)
#define CTCMAC_NOR_AN_D BIT(2)
#define CTCMAC_NOR_LINK_DOWN BIT(1)
#define CTCMAC_NOR_LINK_UP BIT(0)
#define CTCMAC_NOR_RX1_R (1<<7)
#define CTCMAC_NOR_RX0_R (1<<6)
#define CTCMAC_NOR_RX1_D (1<<5)
#define CTCMAC_NOR_RX0_D (1<<4)
#define CTCMAC_NOR_TX_D (1<<3)
#define CTCMAC_NOR_AN_D (1<<2)
#define CTCMAC_NOR_LINK_DOWN (1<<1)
#define CTCMAC_NOR_LINK_UP (1<<0)
#define CTCMAC_FUNC0_RX_D (1<<0)
#define CTCMAC_FUNC0_RX_R (1<<1)
#define CTCMAC_FUNC1_RX_D (1<<0)
#define CTCMAC_FUNC1_RX_R (1<<1)
#define CTC_DDR_BASE 0x80000000
@ -72,25 +76,33 @@
#define CSC_100M 0x02400000
#define CSC_10M 0x18c00000
#define CTCMAC_DESC_INT_NUM 1
#define DESC_INT_COALESCE_CNT_MIN 1
#define DESC_TX_INT_COALESCE_CNT_DEFAULT 16
#define DESC_RX_INT_COALESCE_CNT_DEFAULT 16
/* emu 100us */
//#define CTCMAC_TIMER_THRD 0x4B0
/* board 100us */
#define CTCMAC_TIMER_THRD 0xc350
#define CTCMAC_SUPPORTED (SUPPORTED_10baseT_Full \
| SUPPORTED_100baseT_Full \
| SUPPORTED_1000baseT_Full \
| SUPPORTED_Autoneg)
#define CTCMAC_STATS_LEN (sizeof(struct ctcmac_pkt_stats) / sizeof(u64))
#define CTCMAC_STATS_LEN (sizeof(struct ctcmac_pkt_stats)/sizeof(u64))
struct ctcmac_skb_cb {
unsigned int bytes_sent; /* bytes-on-wire (i.e. no FCB) */
};
#define CTCMAC_CB(skb) ((struct ctcmac_skb_cb *)((skb)->cb))
enum ctcmac_irqinfo_id {
CTCMAC_NORMAL = 0,
CTCMAC_FUNC,
CTCMAC_UNIT,
CTCMAC_FUNC_RX0,
CTCMAC_FUNC_RX1,
CTCMAC_NUM_IRQS
};
@ -105,6 +117,16 @@ enum ctcmac_int_type {
CTCMAC_INT_MAX
};
enum ctcmac_tx_pol_inv {
CTCMAC_TX_POL_INV_DISABLE,
CTCMAC_TX_POL_INV_ENABLE,
};
enum ctcmac_rx_pol_inv {
CTCMAC_RX_POL_INV_DISABLE,
CTCMAC_RX_POL_INV_ENABLE,
};
enum ctcmac_autoneg {
CTCMAC_AUTONEG_1000BASEX_M,
CTCMAC_AUTONEG_PHY_M,
@ -113,34 +135,46 @@ enum ctcmac_autoneg {
CTCMAC_AUTONEG_MAX
};
/* Per TX queue stats */
/*
* Per TX queue stats
*/
struct txq_stats {
unsigned long tx_packets;
unsigned long tx_bytes;
};
struct tx_skb {
struct sk_buff *skb;
int frag_merge;
};
struct ctcmac_tx_buff {
void *vaddr;
dma_addr_t dma;
u32 len;
u32 offset;
u8 alloc;
bool alloc;
};
struct ctcmac_priv_tx_q {
spinlock_t txlock __aligned(SMP_CACHE_BYTES);
spinlock_t txlock __attribute__ ((aligned(SMP_CACHE_BYTES)));
struct ctcmac_tx_buff tx_buff[CTCMAC_MAX_RING_SIZE + 1];
unsigned int num_txbdfree;
u16 tx_ring_size;
u16 qindex;
u16 next_to_alloc;
u16 next_to_clean;
u16 skb_cur;
u16 skb_dirty;
u16 desc_cur;
u16 desc_dirty;
struct txq_stats stats;
struct net_device *dev;
struct sk_buff **tx_skbuff;
struct tx_skb *tx_skbuff;
struct napi_struct napi_tx;
};
/*Per RX queue stats */
/*
* Per RX queue stats
*/
struct rxq_stats {
unsigned long rx_packets;
unsigned long rx_bytes;
@ -167,6 +201,7 @@ struct ctcmac_priv_rx_q {
u32 pps_limit;
u32 token, token_max;
u32 rx_trigger;
struct napi_struct napi_rx;
};
struct ctcmac_irqinfo {
@ -189,9 +224,9 @@ struct ctcmac_private {
struct device *dev;
struct net_device *ndev;
void __iomem *iobase;
struct cpu_mac_regs __iomem *cpumac_reg;
struct cpu_mac_mems __iomem *cpumac_mem;
struct cpu_mac_unit_regs *cpumacu_reg;
struct CpuMac_regs __iomem *cpumac_reg;
struct CpuMac_mems __iomem *cpumac_mem;
struct CpuMacUnit_regs *cpumacu_reg;
u32 device_flags;
int irq_num;
int index;
@ -213,9 +248,10 @@ struct ctcmac_private {
struct work_struct reset_task;
struct platform_device *ofdev;
struct napi_struct napi_rx;
struct napi_struct napi_tx;
struct ctcmac_irqinfo irqinfo[CTCMAC_NUM_IRQS];
struct napi_struct napi_tx;
struct napi_struct napi_rx;
struct napi_struct napi_rx1;
int hwts_rx_en;
int hwts_tx_en;
@ -223,8 +259,16 @@ struct ctcmac_private {
u32 supported;
u32 msg_enable;
u32 int_type;
u32 rx_int_coalesce_cnt;
u32 tx_int_coalesce_cnt;
u8 dfe_enable;
u8 tx_pol_inv;
u8 rx_pol_inv;
struct timer_list token_timer;
u8 version;
u8 pause_aneg_en;
u8 tx_pause_en;
u8 rx_pause_en;
};
struct ctcmac_pkt_stats {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

266
platform/centec-arm64/tsingma-bsp/src/ehci-ctc/ehci-ctc.c Normal file → Executable file
Просмотреть файл

@ -14,13 +14,18 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/sys_soc.h>
#include <linux/timer.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/ehci_pdriver.h>
#include <linux/usb/of.h>
#include "../include/sysctl.h"
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include "ehci.h"
#define DRIVER_DESC "Centec EHCI platform driver"
@ -28,12 +33,15 @@
#define EHCI_MAX_RSTS 4
#define hcd_to_ehci_priv(h) ((struct ehci_ctc_priv *)hcd_to_ehci(h)->priv)
static struct regmap *regmap_base;
struct ehci_ctc_priv {
struct clk *clks[EHCI_MAX_CLKS];
struct reset_control *rsts[EHCI_MAX_RSTS];
struct phy **phys;
int num_phys;
struct reset_control *rsts;
bool reset_on_resume;
bool quirk_poll;
struct timer_list poll_timer;
struct delayed_work poll_work;
};
static const char hcd_name[] = "ehci-ctc";
@ -67,7 +75,7 @@ static int ehci_ctc_power_on(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
int clk, ret, phy_num;
int clk, ret;
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
ret = clk_prepare_enable(priv->clks[clk]);
@ -75,24 +83,8 @@ static int ehci_ctc_power_on(struct platform_device *dev)
goto err_disable_clks;
}
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
ret = phy_init(priv->phys[phy_num]);
if (ret)
goto err_exit_phy;
ret = phy_power_on(priv->phys[phy_num]);
if (ret) {
phy_exit(priv->phys[phy_num]);
goto err_exit_phy;
}
}
return 0;
err_exit_phy:
while (--phy_num >= 0) {
phy_power_off(priv->phys[phy_num]);
phy_exit(priv->phys[phy_num]);
}
err_disable_clks:
while (--clk >= 0)
clk_disable_unprepare(priv->clks[clk]);
@ -104,12 +96,7 @@ static void ehci_ctc_power_off(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
int clk, phy_num;
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
phy_power_off(priv->phys[phy_num]);
phy_exit(priv->phys[phy_num]);
}
int clk;
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
if (priv->clks[clk])
@ -124,22 +111,146 @@ static const struct ehci_driver_overrides platform_overrides __initconst = {
};
static struct usb_ehci_pdata ehci_ctc_defaults = {
.caps_offset = 0x100,
.dma_mask_64 = 1,
.power_on = ehci_ctc_power_on,
.power_suspend = ehci_ctc_power_off,
.power_off = ehci_ctc_power_off,
};
#define KERN_CTC KERN_ERR
static bool quirk_poll_check_port_status(struct ehci_hcd *ehci)
{
u32 port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
if (!(port_status & PORT_OWNER) &&
(port_status & PORT_POWER) &&
!(port_status & PORT_CONNECT) && (port_status & PORT_LS_MASK))
return true;
return false;
}
/**
* quirk_poll_rebind_companion - rebind comanion device to recover
* @ehci: the ehci hcd pointer
*
* Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting
* stuck very rarely after a full/low usb device was disconnected. To
* recover from such a situation, the controllers require changing the OHCI
* functional state.
*/
static void quirk_poll_rebind_companion(struct ehci_hcd *ehci)
{
struct device *companion_dev;
struct usb_hcd *hcd = ehci_to_hcd(ehci);
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (!companion_dev)
return;
device_release_driver(companion_dev);
if (device_attach(companion_dev) < 0)
ehci_err(ehci, "%s: failed\n", __func__);
put_device(companion_dev);
}
static void quirk_poll_work(struct work_struct *work)
{
struct ehci_ctc_priv *priv =
container_of(to_delayed_work(work), struct ehci_ctc_priv,
poll_work);
struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
priv);
/* check the status twice to reduce misdetection rate */
if (!quirk_poll_check_port_status(ehci))
return;
udelay(10);
if (!quirk_poll_check_port_status(ehci))
return;
ehci_dbg(ehci, "%s: detected getting stuck. rebind now!\n", __func__);
quirk_poll_rebind_companion(ehci);
}
static void quirk_poll_timer(struct timer_list *t)
{
struct ehci_ctc_priv *priv = from_timer(priv, t, poll_timer);
struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
priv);
if (quirk_poll_check_port_status(ehci)) {
/*
* Now scheduling the work for testing the port more. Note that
* updating the status is possible to be delayed when
* reconnection. So, this uses delayed work with 5 ms delay
* to avoid misdetection.
*/
schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(5));
}
mod_timer(&priv->poll_timer, jiffies + HZ);
}
static void quirk_poll_init(struct ehci_ctc_priv *priv)
{
INIT_DELAYED_WORK(&priv->poll_work, quirk_poll_work);
timer_setup(&priv->poll_timer, quirk_poll_timer, 0);
mod_timer(&priv->poll_timer, jiffies + HZ);
}
static void quirk_poll_end(struct ehci_ctc_priv *priv)
{
del_timer_sync(&priv->poll_timer);
cancel_delayed_work(&priv->poll_work);
}
static const struct soc_device_attribute quirk_poll_match[] = {
{.family = "R-Car Gen3"},
{ /* sentinel */ }
};
static int ehci_ctc_probe(struct platform_device *dev)
{
u32 val;
struct usb_hcd *hcd;
struct resource *res_mem;
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_ctc_priv *priv;
struct ehci_hcd *ehci;
int err, irq, phy_num, clk = 0, rst;
int err, irq, clk = 0;
regmap_base =
syscon_regmap_lookup_by_phandle(dev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(regmap_base))
return PTR_ERR(regmap_base);
/* USB interface reset config */
val = 0x7f;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &= ~SYS_USB_RESET_CTL_W0_CFG_USB_PHY_PWR_ON_RESET;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &=
~(SYS_USB_RESET_CTL_W0_CFG_USB_PHY_RESET |
SYS_USB_RESET_CTL_W0_CFG_USB_PHY_PORT_RESET |
SYS_USB_RESET_CTL_W0_CFG_USB_UTMI_RESET);
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &=
~(SYS_USB_RESET_CTL_W0_CFG_USB_INTF_RESET |
SYS_USB_RESET_CTL_W0_CFG_USB_AUX_RESET);
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &= ~SYS_USB_RESET_CTL_W0_CFG_USB_PHY_ATE_RESET;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
mdelay(500);
if (usb_disabled())
return -ENODEV;
@ -152,18 +263,17 @@ static int ehci_ctc_probe(struct platform_device *dev)
pdata = &ehci_ctc_defaults;
err = dma_coerce_mask_and_coherent(&dev->dev,
pdata->dma_mask_64 ?
DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
pdata->
dma_mask_64 ? DMA_BIT_MASK(64) :
DMA_BIT_MASK(32));
if (err) {
dev_err(&dev->dev, "Error: DMA mask configuration failed\n");
return err;
}
irq = platform_get_irq(dev, 0);
if (irq < 0) {
dev_err(&dev->dev, "no irq provided");
if (irq < 0)
return irq;
}
hcd = usb_create_hcd(&ehci_ctc_hc_driver, &dev->dev,
dev_name(&dev->dev));
@ -174,6 +284,7 @@ static int ehci_ctc_probe(struct platform_device *dev)
dev->dev.platform_data = pdata;
priv = hcd_to_ehci_priv(hcd);
ehci = hcd_to_ehci(hcd);
if (pdata == &ehci_ctc_defaults && dev->dev.of_node) {
if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
ehci->big_endian_mmio = 1;
@ -192,28 +303,8 @@ static int ehci_ctc_probe(struct platform_device *dev)
"has-transaction-translator"))
hcd->has_tt = 1;
priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
"phys",
"#phy-cells");
if (priv->num_phys > 0) {
priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
sizeof(struct phy *),
GFP_KERNEL);
if (!priv->phys)
return -ENOMEM;
} else
priv->num_phys = 0;
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
priv->phys[phy_num] =
devm_of_phy_get_by_index(&dev->dev,
dev->dev.of_node, phy_num);
if (IS_ERR(priv->phys[phy_num])) {
err = PTR_ERR(priv->phys[phy_num]);
goto err_put_hcd;
}
}
if (soc_device_match(quirk_poll_match))
priv->quirk_poll = true;
for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
@ -226,21 +317,17 @@ static int ehci_ctc_probe(struct platform_device *dev)
}
}
}
for (rst = 0; rst < EHCI_MAX_RSTS; rst++) {
priv->rsts[rst] =
devm_reset_control_get_shared_by_index(&dev->dev, rst);
if (IS_ERR(priv->rsts[rst])) {
err = PTR_ERR(priv->rsts[rst]);
if (err == -EPROBE_DEFER)
goto err_reset;
priv->rsts[rst] = NULL;
break;
}
err = reset_control_deassert(priv->rsts[rst]);
if (err)
goto err_reset;
priv->rsts = devm_reset_control_array_get_optional_shared(&dev->dev);
if (IS_ERR(priv->rsts)) {
err = PTR_ERR(priv->rsts);
goto err_put_clks;
}
err = reset_control_deassert(priv->rsts);
if (err)
goto err_put_clks;
if (pdata->big_endian_desc)
ehci->big_endian_desc = 1;
if (pdata->big_endian_mmio)
@ -272,6 +359,7 @@ static int ehci_ctc_probe(struct platform_device *dev)
if (err < 0)
goto err_reset;
}
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
if (IS_ERR(hcd->regs)) {
@ -280,23 +368,29 @@ static int ehci_ctc_probe(struct platform_device *dev)
}
hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem);
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err_power;
device_wakeup_enable(hcd->self.controller);
device_enable_async_suspend(hcd->self.controller);
platform_set_drvdata(dev, hcd);
if (priv->quirk_poll)
quirk_poll_init(priv);
return err;
err_power:
if (pdata->power_off)
pdata->power_off(dev);
err_reset:
while (--rst >= 0)
reset_control_assert(priv->rsts[rst]);
reset_control_assert(priv->rsts);
err_put_clks:
while (--clk >= 0)
clk_put(priv->clks[clk]);
err_put_hcd:
if (pdata == &ehci_ctc_defaults)
dev->dev.platform_data = NULL;
@ -310,15 +404,17 @@ static int ehci_ctc_remove(struct platform_device *dev)
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
int clk, rst;
int clk;
if (priv->quirk_poll)
quirk_poll_end(priv);
usb_remove_hcd(hcd);
if (pdata->power_off)
pdata->power_off(dev);
for (rst = 0; rst < EHCI_MAX_RSTS && priv->rsts[rst]; rst++)
reset_control_assert(priv->rsts[rst]);
reset_control_assert(priv->rsts);
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
clk_put(priv->clks[clk]);
@ -337,9 +433,13 @@ static int ehci_ctc_suspend(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
bool do_wakeup = device_may_wakeup(dev);
int ret;
if (priv->quirk_poll)
quirk_poll_end(priv);
ret = ehci_suspend(hcd, do_wakeup);
if (ret)
return ret;
@ -356,15 +456,25 @@ static int ehci_ctc_resume(struct device *dev)
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
struct device *companion_dev;
if (pdata->power_on) {
int err = pdata->power_on(pdev);
if (err < 0)
return err;
}
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (companion_dev) {
device_pm_wait_for_dev(hcd->self.controller, companion_dev);
put_device(companion_dev);
}
ehci_resume(hcd, priv->reset_on_resume);
if (priv->quirk_poll)
quirk_poll_init(priv);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
@ -394,7 +504,7 @@ static struct platform_driver ehci_ctc_driver = {
.name = "ehci-ctc",
.pm = &ehci_ctc_pm_ops,
.of_match_table = ctc_ehci_ids,
}
}
};
static int __init ehci_ctc_init(void)

Просмотреть файл

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2001-2002 by David Brownell
*/
@ -31,14 +31,14 @@ typedef __u16 __bitwise __hc16;
struct ehci_stats {
/* irq usage */
unsigned long normal;
unsigned long error;
unsigned long iaa;
unsigned long lost_iaa;
unsigned long normal;
unsigned long error;
unsigned long iaa;
unsigned long lost_iaa;
/* termination of urbs from core */
unsigned long complete;
unsigned long unlink;
unsigned long complete;
unsigned long unlink;
};
/*
@ -46,22 +46,22 @@ struct ehci_stats {
* high-speed devices and full/low-speed devices lying behind a TT.
*/
struct ehci_per_sched {
struct usb_device *udev; /* access to the TT */
struct usb_device *udev; /* access to the TT */
struct usb_host_endpoint *ep;
struct list_head ps_list; /* node on ehci_tt's ps_list */
u16 tt_usecs; /* time on the FS/LS bus */
u16 cs_mask; /* C-mask and S-mask bytes */
u16 period; /* actual period in frames */
u16 phase; /* actual phase, frame part */
u8 bw_phase; /* same, for bandwidth
reservation */
u8 phase_uf; /* uframe part of the phase */
u8 usecs, c_usecs; /* times on the HS bus */
u8 bw_uperiod; /* period in microframes, for
bandwidth reservation */
u8 bw_period; /* same, in frames */
struct list_head ps_list; /* node on ehci_tt's ps_list */
u16 tt_usecs; /* time on the FS/LS bus */
u16 cs_mask; /* C-mask and S-mask bytes */
u16 period; /* actual period in frames */
u16 phase; /* actual phase, frame part */
u8 bw_phase; /* same, for bandwidth
reservation */
u8 phase_uf; /* uframe part of the phase */
u8 usecs, c_usecs; /* times on the HS bus */
u8 bw_uperiod; /* period in microframes, for
bandwidth reservation */
u8 bw_period; /* same, in frames */
};
#define NO_FRAME 29999 /* frame not assigned yet */
#define NO_FRAME 29999 /* frame not assigned yet */
/* ehci_hcd->lock guards shared data against other CPUs:
* ehci_hcd: async, unlink, periodic (and shadow), ...
@ -73,7 +73,7 @@ struct ehci_per_sched {
* when updating hw_* fields in shared qh/qtd/... structures.
*/
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
/*
* ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
@ -92,180 +92,180 @@ enum ehci_rh_state {
* ehci-timer.c) in parallel with this list.
*/
enum ehci_hrtimer_event {
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
EHCI_HRTIMER_ACTIVE_UNLINK, /* Wait while unlinking an active QH */
EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */
EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */
EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
EHCI_HRTIMER_NUM_EVENTS /* Must come last */
EHCI_HRTIMER_NUM_EVENTS /* Must come last */
};
#define EHCI_HRTIMER_NO_EVENT 99
struct ehci_hcd { /* one per controller */
struct ehci_hcd { /* one per controller */
/* timing support */
enum ehci_hrtimer_event next_hrtimer_event;
unsigned enabled_hrtimer_events;
ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
struct hrtimer hrtimer;
enum ehci_hrtimer_event next_hrtimer_event;
unsigned enabled_hrtimer_events;
ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
struct hrtimer hrtimer;
int PSS_poll_count;
int ASS_poll_count;
int died_poll_count;
int PSS_poll_count;
int ASS_poll_count;
int died_poll_count;
/* glue to PCI and HCD framework */
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
struct ehci_dbg_port __iomem *debug;
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
enum ehci_rh_state rh_state;
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
enum ehci_rh_state rh_state;
/* general schedule support */
bool scanning:1;
bool need_rescan:1;
bool intr_unlinking:1;
bool iaa_in_progress:1;
bool async_unlinking:1;
bool shutdown:1;
struct ehci_qh *qh_scan_next;
bool scanning:1;
bool need_rescan:1;
bool intr_unlinking:1;
bool iaa_in_progress:1;
bool async_unlinking:1;
bool shutdown:1;
struct ehci_qh *qh_scan_next;
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *dummy; /* For AMD quirk use */
struct list_head async_unlink;
struct list_head async_idle;
unsigned async_unlink_cycle;
unsigned async_count; /* async activity count */
__hc32 old_current; /* Test for QH becoming */
__hc32 old_token; /* inactive during unlink */
struct ehci_qh *async;
struct ehci_qh *dummy; /* For AMD quirk use */
struct list_head async_unlink;
struct list_head async_idle;
unsigned async_unlink_cycle;
unsigned async_count; /* async activity count */
__hc32 old_current; /* Test for QH becoming */
__hc32 old_token; /* inactive during unlink */
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
__hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
struct list_head intr_qh_list;
unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
struct list_head intr_unlink_wait;
struct list_head intr_unlink;
unsigned intr_unlink_wait_cycle;
unsigned intr_unlink_cycle;
unsigned now_frame; /* frame from HC hardware */
unsigned last_iso_frame; /* last frame scanned for iso */
unsigned intr_count; /* intr activity count */
unsigned isoc_count; /* isoc activity count */
unsigned periodic_count; /* periodic activity count */
unsigned uframe_periodic_max; /* max periodic time per uframe */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
__hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
struct list_head intr_qh_list;
unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
struct list_head intr_unlink_wait;
struct list_head intr_unlink;
unsigned intr_unlink_wait_cycle;
unsigned intr_unlink_cycle;
unsigned now_frame; /* frame from HC hardware */
unsigned last_iso_frame; /* last frame scanned for iso */
unsigned intr_count; /* intr activity count */
unsigned isoc_count; /* isoc activity count */
unsigned periodic_count; /* periodic activity count */
unsigned uframe_periodic_max; /* max periodic time per uframe */
/* list of itds & sitds completed while now_frame was still active */
struct list_head cached_itd_list;
struct ehci_itd *last_itd_to_free;
struct list_head cached_sitd_list;
struct ehci_sitd *last_sitd_to_free;
struct list_head cached_itd_list;
struct ehci_itd *last_itd_to_free;
struct list_head cached_sitd_list;
struct ehci_sitd *last_sitd_to_free;
/* per root hub port */
unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
/* bit vectors (one bit per port) */
unsigned long bus_suspended; /* which ports were
already suspended at the start of a bus suspend */
unsigned long companion_ports; /* which ports are
dedicated to the companion controller */
unsigned long owned_ports; /* which ports are
owned by the companion during a bus suspend */
unsigned long port_c_suspend; /* which ports have
the change-suspend feature turned on */
unsigned long suspended_ports; /* which ports are
suspended */
unsigned long resuming_ports; /* which ports have
started to resume */
unsigned long bus_suspended; /* which ports were
already suspended at the start of a bus suspend */
unsigned long companion_ports; /* which ports are
dedicated to the companion controller */
unsigned long owned_ports; /* which ports are
owned by the companion during a bus suspend */
unsigned long port_c_suspend; /* which ports have
the change-suspend feature turned on */
unsigned long suspended_ports; /* which ports are
suspended */
unsigned long resuming_ports; /* which ports have
started to resume */
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
unsigned random_frame;
unsigned long next_statechange;
ktime_t last_periodic_enable;
u32 command;
unsigned random_frame;
unsigned long next_statechange;
ktime_t last_periodic_enable;
u32 command;
/* SILICON QUIRKS */
unsigned no_selective_suspend:1;
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */
unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
unsigned big_endian_capbase:1;
unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1;
unsigned amd_pll_fix:1;
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
unsigned need_oc_pp_cycle:1; /* MPC834X port power */
unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
unsigned no_selective_suspend:1;
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */
unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
unsigned big_endian_capbase:1;
unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1;
unsigned amd_pll_fix:1;
unsigned use_dummy_qh:1; /* AMD Frame List table quirk */
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
unsigned need_oc_pp_cycle:1; /* MPC834X port power */
unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
#define OHCI_USB_OPER (2 << 6)
#define OHCI_USB_SUSPEND (3 << 6)
#define OHCI_CTRL_HCFS (3 << 6)
#define OHCI_USB_OPER (2 << 6)
#define OHCI_USB_SUSPEND (3 << 6)
#define OHCI_HCCTRL_OFFSET 0x4
#define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1;
unsigned has_tdi_phy_lpm:1;
unsigned has_ppcd:1; /* support per-port change bits */
u8 sbrn; /* packed release number */
#define OHCI_HCCTRL_OFFSET 0x4
#define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1;
unsigned has_tdi_phy_lpm:1;
unsigned has_ppcd:1; /* support per-port change bits */
u8 sbrn; /* packed release number */
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
# define COUNT(x) ((x)++)
struct ehci_stats stats;
# define INCR(x) ((x)++)
#else
# define COUNT(x)
# define INCR(x) do {} while (0)
#endif
/* debug files */
#ifdef CONFIG_DYNAMIC_DEBUG
struct dentry *debug_dir;
struct dentry *debug_dir;
#endif
/* bandwidth usage */
#define EHCI_BANDWIDTH_SIZE 64
#define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3)
u8 bandwidth[EHCI_BANDWIDTH_SIZE];
/* us allocated per uframe */
u8 tt_budget[EHCI_BANDWIDTH_SIZE];
/* us budgeted per uframe */
struct list_head tt_list;
u8 bandwidth[EHCI_BANDWIDTH_SIZE];
/* us allocated per uframe */
u8 tt_budget[EHCI_BANDWIDTH_SIZE];
/* us budgeted per uframe */
struct list_head tt_list;
/* platform-specific data -- must come last */
unsigned long priv[0] __aligned(sizeof(s64));
unsigned long priv[] __aligned(sizeof(s64));
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd)
{
return (struct ehci_hcd *) (hcd->hcd_priv);
return (struct ehci_hcd *)(hcd->hcd_priv);
}
static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
{
return container_of((void *) ehci, struct usb_hcd, hcd_priv);
return container_of((void *)ehci, struct usb_hcd, hcd_priv);
}
/*-------------------------------------------------------------------------*/
@ -286,9 +286,9 @@ static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
*/
struct ehci_qtd {
/* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.5.1 */
__hc32 hw_alt_next; /* see EHCI 3.5.2 */
__hc32 hw_token; /* see EHCI 3.5.3 */
__hc32 hw_next; /* see EHCI 3.5.1 */
__hc32 hw_alt_next; /* see EHCI 3.5.2 */
__hc32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
@ -307,14 +307,14 @@ struct ehci_qtd {
#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)
#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)
__hc32 hw_buf[5]; /* see EHCI 3.5.4 */
__hc32 hw_buf_hi[5]; /* Appendix B */
__hc32 hw_buf[5]; /* see EHCI 3.5.4 */
__hc32 hw_buf_hi[5]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
} __aligned(32);
/* mask NakCnt+T in qh->hw_alt_next */
@ -345,7 +345,7 @@ struct ehci_qtd {
(cpu_to_hc32(ehci, (((u32) dma) & ~0x01f) | Q_TYPE_QH))
/* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
/*
* Entries in periodic shadow table are pointers to one of four kinds
@ -356,12 +356,12 @@ struct ehci_qtd {
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
__hc32 *hw_next; /* (all types) */
void *ptr;
struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
__hc32 *hw_next; /* (all types) */
void *ptr;
};
/*-------------------------------------------------------------------------*/
@ -376,8 +376,8 @@ union ehci_shadow {
/* first part defined by EHCI spec */
struct ehci_qh_hw {
__hc32 hw_next; /* see EHCI 3.6.1 */
__hc32 hw_info1; /* see EHCI 3.6.2 */
__hc32 hw_next; /* see EHCI 3.6.1 */
__hc32 hw_info1; /* see EHCI 3.6.2 */
#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
#define QH_HEAD (1 << 15) /* Head of async reclamation list */
#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
@ -385,59 +385,59 @@ struct ehci_qh_hw {
#define QH_LOW_SPEED (1 << 12)
#define QH_FULL_SPEED (0 << 12)
#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
__hc32 hw_info2; /* see EHCI 3.6.2 */
__hc32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__hc32 hw_qtd_next;
__hc32 hw_alt_next;
__hc32 hw_token;
__hc32 hw_buf[5];
__hc32 hw_buf_hi[5];
__hc32 hw_qtd_next;
__hc32 hw_alt_next;
__hc32 hw_token;
__hc32 hw_buf[5];
__hc32 hw_buf_hi[5];
} __aligned(32);
struct ehci_qh {
struct ehci_qh_hw *hw; /* Must come first */
struct ehci_qh_hw *hw; /* Must come first */
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct list_head intr_node; /* list of intr QHs */
struct ehci_qtd *dummy;
struct list_head unlink_node;
struct ehci_per_sched ps; /* scheduling info */
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct list_head intr_node; /* list of intr QHs */
struct ehci_qtd *dummy;
struct list_head unlink_node;
struct ehci_per_sched ps; /* scheduling info */
unsigned unlink_cycle;
unsigned unlink_cycle;
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
u8 xacterrs; /* XactErr retry counter */
#define QH_XACTERR_MAX 32 /* XactErr retry limit */
u8 xacterrs; /* XactErr retry counter */
#define QH_XACTERR_MAX 32 /* XactErr retry limit */
u8 unlink_reason;
#define QH_UNLINK_HALTED 0x01 /* Halt flag is set */
#define QH_UNLINK_SHORT_READ 0x02 /* Recover from a short read */
#define QH_UNLINK_DUMMY_OVERLAY 0x04 /* QH overlayed the dummy TD */
#define QH_UNLINK_SHUTDOWN 0x08 /* The HC isn't running */
#define QH_UNLINK_QUEUE_EMPTY 0x10 /* Reached end of the queue */
#define QH_UNLINK_REQUESTED 0x20 /* Disable, reset, or dequeue */
u8 unlink_reason;
#define QH_UNLINK_HALTED 0x01 /* Halt flag is set */
#define QH_UNLINK_SHORT_READ 0x02 /* Recover from a short read */
#define QH_UNLINK_DUMMY_OVERLAY 0x04 /* QH overlayed the dummy TD */
#define QH_UNLINK_SHUTDOWN 0x08 /* The HC isn't running */
#define QH_UNLINK_QUEUE_EMPTY 0x10 /* Reached end of the queue */
#define QH_UNLINK_REQUESTED 0x20 /* Disable, reset, or dequeue */
u8 gap_uf; /* uframes split/csplit gap */
u8 gap_uf; /* uframes split/csplit gap */
unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
unsigned dequeue_during_giveback:1;
unsigned should_be_inactive:1;
unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
unsigned dequeue_during_giveback:1;
unsigned should_be_inactive:1;
};
/*-------------------------------------------------------------------------*/
@ -445,11 +445,11 @@ struct ehci_qh {
/* description of one iso transaction (up to 3 KB data if highspeed) */
struct ehci_iso_packet {
/* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
__hc32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
__hc32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
/* for full speed OUT splits */
u32 buf1;
u32 buf1;
};
/* temporary schedule data for packets from iso urbs (both speeds)
@ -457,10 +457,10 @@ struct ehci_iso_packet {
* beginning at stream->next_uframe
*/
struct ehci_iso_sched {
struct list_head td_list;
unsigned span;
unsigned first_packet;
struct ehci_iso_packet packet[0];
struct list_head td_list;
unsigned span;
unsigned first_packet;
struct ehci_iso_packet packet[];
};
/*
@ -469,32 +469,32 @@ struct ehci_iso_sched {
*/
struct ehci_iso_stream {
/* first field matches ehci_hq, but is NULL */
struct ehci_qh_hw *hw;
struct ehci_qh_hw *hw;
u8 bEndpointAddress;
u8 highspeed;
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
u8 bEndpointAddress;
u8 highspeed;
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
/* output of (re)scheduling */
struct ehci_per_sched ps; /* scheduling info */
unsigned next_uframe;
__hc32 splits;
struct ehci_per_sched ps; /* scheduling info */
unsigned next_uframe;
__hc32 splits;
/* the rest is derived from the endpoint descriptor,
* including the extra info for hw_bufp[0..2]
*/
u16 uperiod; /* period in uframes */
u16 maxp;
unsigned bandwidth;
u16 uperiod; /* period in uframes */
u16 maxp;
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
__hc32 buf0;
__hc32 buf1;
__hc32 buf2;
__hc32 buf0;
__hc32 buf1;
__hc32 buf2;
/* this is used to initialize sITD's tt info */
__hc32 address;
__hc32 address;
};
/*-------------------------------------------------------------------------*/
@ -507,32 +507,32 @@ struct ehci_iso_stream {
*/
struct ehci_itd {
/* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.3.1 */
__hc32 hw_transaction[8]; /* see EHCI 3.3.2 */
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
__hc32 hw_next; /* see EHCI 3.3.1 */
__hc32 hw_transaction[8]; /* see EHCI 3.3.2 */
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
__hc32 hw_bufp[7]; /* see EHCI 3.3.3 */
__hc32 hw_bufp_hi[7]; /* Appendix B */
__hc32 hw_bufp[7]; /* see EHCI 3.3.3 */
__hc32 hw_bufp_hi[7]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t itd_dma; /* for this itd */
union ehci_shadow itd_next; /* ptr to periodic q entry */
dma_addr_t itd_dma; /* for this itd */
union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */
struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */
/* any/all hw_transactions here may be used by that urb */
unsigned frame; /* where scheduled */
unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */
unsigned frame; /* where scheduled */
unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */
} __aligned(32);
/*-------------------------------------------------------------------------*/
@ -545,11 +545,11 @@ struct ehci_itd {
*/
struct ehci_sitd {
/* first part defined by EHCI spec */
__hc32 hw_next;
__hc32 hw_next;
/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
__hc32 hw_fullspeed_ep; /* EHCI table 3-9 */
__hc32 hw_uframe; /* EHCI table 3-10 */
__hc32 hw_results; /* EHCI table 3-11 */
__hc32 hw_fullspeed_ep; /* EHCI table 3-9 */
__hc32 hw_uframe; /* EHCI table 3-10 */
__hc32 hw_results; /* EHCI table 3-11 */
#define SITD_IOC (1 << 31) /* interrupt on completion */
#define SITD_PAGE (1 << 30) /* buffer 0/1 */
#define SITD_LENGTH(x) (((x) >> 16) & 0x3ff)
@ -563,19 +563,19 @@ struct ehci_sitd {
#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE)
__hc32 hw_buf[2]; /* EHCI table 3-12 */
__hc32 hw_backpointer; /* EHCI table 3-13 */
__hc32 hw_buf_hi[2]; /* Appendix B */
__hc32 hw_buf[2]; /* EHCI table 3-12 */
__hc32 hw_backpointer; /* EHCI table 3-13 */
__hc32 hw_buf_hi[2]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t sitd_dma;
union ehci_shadow sitd_next; /* ptr to periodic q entry */
dma_addr_t sitd_dma;
union ehci_shadow sitd_next; /* ptr to periodic q entry */
struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head sitd_list; /* list of stream's sitds */
unsigned frame;
unsigned index;
struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head sitd_list; /* list of stream's sitds */
unsigned frame;
unsigned index;
} __aligned(32);
/*-------------------------------------------------------------------------*/
@ -590,12 +590,12 @@ struct ehci_sitd {
* it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
*/
struct ehci_fstn {
__hc32 hw_next; /* any periodic q entry */
__hc32 hw_prev; /* qh or EHCI_LIST_END */
__hc32 hw_next; /* any periodic q entry */
__hc32 hw_prev; /* qh or EHCI_LIST_END */
/* the rest is HCD-private */
dma_addr_t fstn_dma;
union ehci_shadow fstn_next; /* ptr to periodic q entry */
dma_addr_t fstn_dma;
union ehci_shadow fstn_next; /* ptr to periodic q entry */
} __aligned(32);
/*-------------------------------------------------------------------------*/
@ -619,12 +619,12 @@ struct ehci_fstn {
*/
struct ehci_tt {
u16 bandwidth[EHCI_BANDWIDTH_FRAMES];
u16 bandwidth[EHCI_BANDWIDTH_FRAMES];
struct list_head tt_list; /* List of all ehci_tt's */
struct list_head ps_list; /* Items using this TT */
struct usb_tt *usb_tt;
int tt_port; /* TT port number */
struct list_head tt_list; /* List of all ehci_tt's */
struct list_head ps_list; /* Items using this TT */
struct usb_tt *usb_tt;
int tt_port; /* TT port number */
};
/*-------------------------------------------------------------------------*/
@ -736,12 +736,10 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
#endif
static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
__u32 __iomem *regs)
__u32 __iomem * regs)
{
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
return ehci_big_endian_mmio(ehci) ?
readl_be(regs) :
readl(regs);
return ehci_big_endian_mmio(ehci) ? readl_be(regs) : readl(regs);
#else
return readl(regs);
#endif
@ -749,23 +747,21 @@ static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
#ifdef CONFIG_SOC_IMX28
static inline void imx28_ehci_writel(const unsigned int val,
volatile __u32 __iomem *addr)
volatile __u32 __iomem * addr)
{
__asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr));
__asm__("swp %0, %0, [%1]": :"r"(val), "r"(addr));
}
#else
static inline void imx28_ehci_writel(const unsigned int val,
volatile __u32 __iomem *addr)
volatile __u32 __iomem * addr)
{
}
#endif
static inline void ehci_writel(const struct ehci_hcd *ehci,
const unsigned int val, __u32 __iomem *regs)
const unsigned int val, __u32 __iomem * regs)
{
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
ehci_big_endian_mmio(ehci) ?
writel_be(val, regs) :
writel(val, regs);
ehci_big_endian_mmio(ehci) ? writel_be(val, regs) : writel(val, regs);
#else
if (ehci->imx28_write_fix)
imx28_ehci_writel(val, regs);
@ -791,11 +787,12 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
hc_control |= OHCI_USB_SUSPEND;
writel_be(hc_control, ehci->ohci_hcctrl_reg);
(void) readl_be(ehci->ohci_hcctrl_reg);
(void)readl_be(ehci->ohci_hcctrl_reg);
}
#else
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
{ }
{
}
#endif
/*-------------------------------------------------------------------------*/
@ -814,23 +811,23 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x)
{
return ehci_big_endian_desc(ehci)
? (__force __hc32)cpu_to_be32(x)
: (__force __hc32)cpu_to_le32(x);
? (__force __hc32) cpu_to_be32(x)
: (__force __hc32) cpu_to_le32(x);
}
/* ehci to cpu */
static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x)
{
return ehci_big_endian_desc(ehci)
? be32_to_cpu((__force __be32)x)
: le32_to_cpu((__force __le32)x);
? be32_to_cpu((__force __be32) x)
: le32_to_cpu((__force __le32) x);
}
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 * x)
{
return ehci_big_endian_desc(ehci)
? be32_to_cpup((__force __be32 *)x)
: le32_to_cpup((__force __le32 *)x);
? be32_to_cpup((__force __be32 *) x)
: le32_to_cpup((__force __le32 *) x);
}
#else
@ -847,7 +844,7 @@ static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x)
return le32_to_cpu(x);
}
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 * x)
{
return le32_to_cpup(x);
}
@ -870,25 +867,24 @@ static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
/* Declarations of things exported for use by ehci platform drivers */
struct ehci_driver_overrides {
size_t extra_priv_size;
int (*reset)(struct usb_hcd *hcd);
int (*port_power)(struct usb_hcd *hcd,
int portnum, bool enable);
size_t extra_priv_size;
int (*reset) (struct usb_hcd * hcd);
int (*port_power) (struct usb_hcd * hcd, int portnum, bool enable);
};
extern void ehci_init_driver(struct hc_driver *drv,
const struct ehci_driver_overrides *over);
extern int ehci_setup(struct usb_hcd *hcd);
extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
u32 mask, u32 done, int usec);
extern int ehci_reset(struct ehci_hcd *ehci);
extern void ehci_init_driver(struct hc_driver *drv,
const struct ehci_driver_overrides *over);
extern int ehci_setup(struct usb_hcd *hcd);
extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem * ptr,
u32 mask, u32 done, int usec);
extern int ehci_reset(struct ehci_hcd *ehci);
extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
bool suspending, bool do_wakeup);
extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
bool suspending, bool do_wakeup);
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength);
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength);
#endif /* __LINUX_EHCI_HCD_H */

Просмотреть файл

@ -25,12 +25,19 @@
#include <linux/spinlock.h>
#include "gpio-ctcapb.h"
#include <linux/slab.h>
#include <linux/delay.h>
#include "gpiolib.h"
#include "../include/sysctl.h"
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include "gpiolib-acpi.h"
#define DWAPB_MAX_PORTS 2
struct ctcapb_gpio;
static u32 soc_v;
struct ctcapb_gpio_port {
bool is_registered;
unsigned int idx;
@ -45,14 +52,15 @@ struct ctcapb_gpio {
unsigned int nr_ports;
struct GpioSoc_regs *regs;
struct ctcapb_gpio_port *ports;
struct regmap *regmap_base;
};
static void clrsetbits(unsigned __iomem *addr, u32 clr, u32 set)
static void clrsetbits(unsigned __iomem * addr, u32 clr, u32 set)
{
writel((readl(addr) & ~(clr)) | (set), addr);
}
static int ctcapb_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
static int ctcapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct ctcapb_gpio_port *port = gpiochip_get_data(gc);
@ -123,6 +131,20 @@ static void ctcapb_irq_unmask(struct irq_data *d)
ctcapb_irq_enable(d);
}
#if 0
static void ctcapb_irq_disable(struct irq_data *d)
{
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
struct ctcapb_gpio_port *port = igc->private;
struct gpio_chip *gc = &port->gc;
unsigned long flags;
spin_lock_irqsave(&gc->bgpio_lock, flags);
clrsetbits(&port->regs->GpioIntrEn, ~BIT(d->hwirq), 0);
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
#endif
static int ctcapb_irq_reqres(struct irq_data *d)
{
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
@ -152,7 +174,7 @@ static int ctcapb_irq_set_type(struct irq_data *d, u32 type)
struct ctcapb_gpio_port *port = igc->private;
struct gpio_chip *gc = &port->gc;
int bit = d->hwirq;
unsigned long level, polarity, flags;
unsigned long level, polarity, flags, datactl, outctl;
if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
@ -162,6 +184,21 @@ static int ctcapb_irq_set_type(struct irq_data *d, u32 type)
level = readl(&port->regs->GpioIntrLevel);
polarity = readl(&port->regs->GpioIntrPolarity);
if (!soc_v) {
datactl = readl(&port->regs->GpioDataCtl);
outctl = readl(&port->regs->GpioOutCtl);
datactl &= ~BIT(bit);
outctl |= BIT(bit);
writel(datactl, &port->regs->GpioDataCtl);
writel(outctl, &port->regs->GpioOutCtl);
udelay(10);
outctl &= ~BIT(bit);
writel(outctl, &port->regs->GpioOutCtl);
}
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
level &= ~BIT(bit);
@ -195,7 +232,7 @@ static int ctcapb_irq_set_type(struct irq_data *d, u32 type)
}
static int ctcapb_gpio_set_debounce(struct gpio_chip *gc,
unsigned int offset, unsigned int debounce)
unsigned offset, unsigned debounce)
{
struct ctcapb_gpio_port *port = gpiochip_get_data(gc);
unsigned long flags, val_deb;
@ -445,7 +482,8 @@ static struct ctcapb_platform_data *ctcapb_gpio_get_pdata(struct device *dev)
}
if (dev->of_node && fwnode_property_read_bool(fwnode,
"interrupt-controller")) {
"interrupt-controller"))
{
pp->irq = irq_of_parse_and_map(to_of_node(fwnode), 0);
if (!pp->irq)
dev_warn(dev, "no irq for port%d\n", pp->idx);
@ -461,12 +499,42 @@ static struct ctcapb_platform_data *ctcapb_gpio_get_pdata(struct device *dev)
return pdata;
}
int ctc_bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
unsigned long mask;
if (gc->be_bits)
mask = BIT(gc->bgpio_bits - 1 - gpio);
else
mask = BIT(gpio);
if (soc_v) {
return 0;
} else {
if (val)
gc->bgpio_data |= mask;
else
gc->bgpio_data &= ~mask;
gc->write_reg(gc->reg_set, gc->bgpio_data);
if (gc->be_bits)
gc->bgpio_dir |= BIT(gc->bgpio_bits - 1 - gpio);
else
gc->bgpio_dir |= BIT(gpio);
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
return 0;
}
}
static int ctcapb_gpio_probe(struct platform_device *pdev)
{
unsigned int i;
struct resource *res;
struct ctcapb_gpio *gpio;
int err;
int err, val;
struct device *dev = &pdev->dev;
struct ctcapb_platform_data *pdata = dev_get_platdata(dev);
@ -491,6 +559,18 @@ static int ctcapb_gpio_probe(struct platform_device *pdev)
if (!gpio->ports)
return -ENOMEM;
gpio->regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(gpio->regmap_base))
return PTR_ERR(gpio->regmap_base);
regmap_read(gpio->regmap_base,
offsetof(struct SysCtl_regs, SysCtlSysRev), &val);
soc_v = val;
printk("soc version = 0x%x\n", soc_v);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio->regs =
(struct GpioSoc_regs *)devm_ioremap_resource(&pdev->dev, res);
@ -508,8 +588,9 @@ static int ctcapb_gpio_probe(struct platform_device *pdev)
out_unregister:
ctcapb_gpio_unregister(gpio);
for (i = 0; i < gpio->nr_ports; i++)
for (i = 0; i < gpio->nr_ports; i++) {
ctcapb_irq_teardown(&gpio->ports[i]);
}
return err;
}
@ -520,8 +601,9 @@ static int ctcapb_gpio_remove(struct platform_device *pdev)
struct ctcapb_gpio *gpio = platform_get_drvdata(pdev);
ctcapb_gpio_unregister(gpio);
for (i = 0; i < gpio->nr_ports; i++)
for (i = 0; i < gpio->nr_ports; i++) {
ctcapb_irq_teardown(&gpio->ports[i]);
}
return 0;
}

Просмотреть файл

@ -1,27 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Internal GPIO functions.
*
* Copyright (C) 2013, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.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.
*/
#ifndef GPIOLIB_H
#define GPIOLIB_H
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/err.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/cdev.h>
enum of_gpio_flags;
enum gpio_lookup_flags;
struct acpi_device;
#define GPIOCHIP_NAME "gpiochip"
/**
* struct gpio_device - internal state container for GPIO devices
@ -49,18 +44,19 @@ struct acpi_device;
* userspace.
*/
struct gpio_device {
int id;
struct device dev;
struct cdev chrdev;
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip;
struct gpio_desc *descs;
int base;
u16 ngpio;
const char *label;
void *data;
struct list_head list;
int id;
struct device dev;
struct cdev chrdev;
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip;
struct gpio_desc *descs;
int base;
u16 ngpio;
const char *label;
void *data;
struct list_head list;
struct blocking_notifier_head notifier;
#ifdef CONFIG_PINCTRL
/*
@ -73,138 +69,36 @@ struct gpio_device {
#endif
};
/**
* struct acpi_gpio_info - ACPI GPIO specific information
* @adev: reference to ACPI device which consumes GPIO resource
* @flags: GPIO initialization flags
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
* @polarity: interrupt polarity as provided by ACPI
* @triggering: triggering type as provided by ACPI
* @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
*/
struct acpi_gpio_info {
struct acpi_device *adev;
enum gpiod_flags flags;
bool gpioint;
int polarity;
int triggering;
unsigned int quirks;
/* gpio suffixes used for ACPI and device tree lookup */
static __maybe_unused const char *const gpio_suffixes[] = { "gpios", "gpio" };
struct gpio_array {
struct gpio_desc **desc;
unsigned int size;
struct gpio_chip *chip;
unsigned long *get_mask;
unsigned long *set_mask;
unsigned long invert_mask[];
};
/* gpio suffixes used for ACPI and device tree lookup */
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
#ifdef CONFIG_OF_GPIO
struct gpio_desc *of_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpio_lookup_flags *flags);
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *list_name, int index, enum of_gpio_flags *flags);
int of_gpiochip_add(struct gpio_chip *gc);
void of_gpiochip_remove(struct gpio_chip *gc);
#else
static inline struct gpio_desc *of_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpio_lookup_flags *flags)
{
return ERR_PTR(-ENOENT);
}
static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *list_name, int index, enum of_gpio_flags *flags)
{
return ERR_PTR(-ENOENT);
}
static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; }
static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
#endif /* CONFIG_OF_GPIO */
#ifdef CONFIG_ACPI
void acpi_gpiochip_add(struct gpio_chip *chip);
void acpi_gpiochip_remove(struct gpio_chip *chip);
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
struct acpi_gpio_info *info);
struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags);
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index,
struct acpi_gpio_info *info);
int acpi_gpio_count(struct device *dev, const char *con_id);
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
#else
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
static inline void
acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
static inline void
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
static inline int
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
{
return 0;
}
static inline struct gpio_desc *
acpi_find_gpio(struct device *dev, const char *con_id,
unsigned int idx, enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags)
{
return ERR_PTR(-ENOENT);
}
static inline struct gpio_desc *
acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
int index, struct acpi_gpio_info *info)
{
return ERR_PTR(-ENXIO);
}
static inline int acpi_gpio_count(struct device *dev, const char *con_id)
{
return -ENODEV;
}
static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
const char *con_id)
{
return false;
}
#endif
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
const char *propname, int index,
enum gpiod_flags dflags,
const char *label);
extern struct spinlock gpio_lock;
extern spinlock_t gpio_lock;
extern struct list_head gpio_devices;
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
@ -214,21 +108,34 @@ struct gpio_desc {
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
/* Connection label */
const char *label;
const char *label;
/* Name of the GPIO */
const char *name;
const char *name;
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
#ifdef CONFIG_GPIO_CDEV
/* debounce period in microseconds */
unsigned int debounce_period_us;
#endif
};
int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
unsigned long lflags, enum gpiod_flags dflags);
unsigned long lflags, enum gpiod_flags dflags);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags);
unsigned long lflags, enum gpiod_flags dflags);
/*
* Return the GPIO number of the passed descriptor relative to its chip
@ -238,9 +145,6 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
return desc - &desc->gdev->descs[0];
}
void devprop_gpiochip_set_names(struct gpio_chip *chip,
const struct fwnode_handle *fwnode);
/* With descriptor prefix */
#define gpiod_emerg(desc, fmt, ...) \
@ -264,35 +168,17 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip,
/* With chip prefix */
#define chip_emerg(chip, fmt, ...) \
dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_crit(chip, fmt, ...) \
dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_err(chip, fmt, ...) \
dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_warn(chip, fmt, ...) \
dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_info(chip, fmt, ...) \
dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_dbg(chip, fmt, ...) \
dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#ifdef CONFIG_GPIO_SYSFS
int gpiochip_sysfs_register(struct gpio_device *gdev);
void gpiochip_sysfs_unregister(struct gpio_device *gdev);
#else
static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
{
return 0;
}
static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
}
#endif /* CONFIG_GPIO_SYSFS */
#define chip_emerg(gc, fmt, ...) \
dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_crit(gc, fmt, ...) \
dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_err(gc, fmt, ...) \
dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_warn(gc, fmt, ...) \
dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_info(gc, fmt, ...) \
dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_dbg(gc, fmt, ...) \
dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#endif /* GPIOLIB_H */

147
platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c Normal file → Executable file
Просмотреть файл

@ -1,4 +1,5 @@
/* Centec I2C controller driver
/*
* Centec I2C controller driver
*
* Author: Wangyb <wangyb@centecnetworks.com>
*
@ -20,6 +21,10 @@
#include <linux/io.h>
#include <linux/export.h>
#include "i2c-ctc.h"
#include "../pinctrl-ctc/pinctrl-ctc.h"
#include "../include/sysctl.h"
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#define IC_ICK_NS(f) (1000000000 / f)
@ -30,7 +35,7 @@ static char *abort_sources[] = {
[ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
[ABRT_TXDATA_NOACK] = "data not acknowledged",
[ABRT_GCALL_NOACK] = "no acknowledgment for a general call",
[ABRT_GCALL_NOACK] = "no acknowledgement for a general call",
[ABRT_GCALL_READ] = "read after general call",
[ABRT_SBYTE_ACKDET] = "start byte acknowledged",
[ABRT_SBYTE_NORSTRT] =
@ -99,8 +104,7 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
__i2c_ctc_enable(dev, false);
/* Set SCL timing parameters */
if ((dev->master_cfg & CTC_IC_CON_SPEED_MASK)
== CTC_IC_CON_SPEED_FAST) {
if ((dev->master_cfg & CTC_IC_CON_SPEED_MASK) == CTC_IC_CON_SPEED_FAST) {
hcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 14 - 4;
lcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 1 - 2;
@ -122,8 +126,9 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
}
/* Configure SDA Hold Time if required */
if (dev->sda_hold_time)
if (dev->sda_hold_time) {
ctc_writel(dev, dev->sda_hold_time, CTC_IC_SDA_HOLD);
}
/* Configure Tx/Rx FIFO threshold levels */
comp_param1 = ctc_readl(dev, CTC_IC_COMP_PARAM_1);
@ -139,6 +144,33 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
return 0;
}
int i2c_ctc_recover_bus(struct i2c_adapter *adap)
{
struct ctc_i2c_dev *dev = i2c_get_adapdata(adap);
u32 val = 0;
dev_info(dev->dev, "Trying i2c bus recovery\n");
if (dev->i2c_num == 0)
val = 0x1;
if (dev->i2c_num == 1)
val = 0x2;
regmap_write(dev->regmap_base,
offsetof(struct SysCtl_regs, SysI2CResetCtl), val);
val = 0x0;
regmap_write(dev->regmap_base,
offsetof(struct SysCtl_regs, SysI2CResetCtl), val);
if (dev->soc_ver == CTC_REV_TM_1_1) {
ctc_writel(dev, 0x1, CTC_IC_BUS_CLEAR_EN);
}
i2c_ctc_init(dev);
return 0;
}
static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
{
int timeout = 20;
@ -146,7 +178,15 @@ static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
while (ctc_readl(dev, CTC_IC_STATUS) & CTC_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
i2c_recover_bus(&dev->adapter);
if (ctc_readl(dev, CTC_IC_STATUS) &
CTC_IC_STATUS_ACTIVITY) {
dev_warn(dev->dev,
"timeout waiting for bus ready again\n");
return -ETIMEDOUT;
}
return 0;
}
timeout--;
usleep_range(1000, 1100);
@ -154,6 +194,16 @@ static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
return 0;
}
void i2c_ctc_disable(struct ctc_i2c_dev *dev)
{
/* Disable controller */
__i2c_ctc_enable(dev, false);
/* Disable all interupts */
ctc_writel(dev, 0, CTC_IC_INTR_MASK);
ctc_readl(dev, CTC_IC_CLR_INTR);
}
void i2c_ctc_disable_intr(struct ctc_i2c_dev *dev)
{
ctc_writel(dev, 0, CTC_IC_INTR_MASK);
@ -189,7 +239,7 @@ static void i2c_ctc_xfer_init(struct ctc_i2c_dev *dev)
/* Clear and enable interrupts */
ctc_readl(dev, CTC_IC_CLR_INTR);
ctc_writel(dev, CTC_IC_INTR_DEFAULT_MASK, CTC_IC_INTR_MASK);
ctc_writel(dev, CTC_IC_INTR_MASTER_MASK, CTC_IC_INTR_MASK);
}
static int i2c_ctc_handle_tx_abort(struct ctc_i2c_dev *dev)
@ -199,13 +249,12 @@ static int i2c_ctc_handle_tx_abort(struct ctc_i2c_dev *dev)
if (abort_source & CTC_IC_TX_ABRT_NOACK) {
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_dbg(dev->dev, "%s: %s\n", __func__,
abort_sources[i]);
dev_dbg(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
return -EREMOTEIO;
}
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
if (abort_source & CTC_IC_TX_ARB_LOST)
return -EAGAIN;
@ -241,7 +290,7 @@ static int i2c_ctc_interrupt_transfer(struct ctc_i2c_dev *dev)
/* wait for tx to complete */
if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) {
dev_err(dev->dev, "controller timed out\n");
i2c_ctc_init(dev);
i2c_recover_bus(&dev->adapter);
ret = -ETIMEDOUT;
goto done;
}
@ -287,8 +336,7 @@ static int ctc_i2c_xfer_finish(struct ctc_i2c_dev *dev)
CTC_IC_INTR_STOP_DET)) {
ctc_readl(dev, CTC_IC_CLR_STOP_DET);
break;
} else if (time_after(jiffies, start_stop_det +
I2C_STOPDET_TO)) {
} else if (time_after(jiffies, start_stop_det + I2C_STOPDET_TO)) {
break;
}
}
@ -302,8 +350,8 @@ static int ctc_i2c_xfer_finish(struct ctc_i2c_dev *dev)
return 0;
}
static int __ctc_i2c_read(struct ctc_i2c_dev *dev, __u16 chip_addr, u8 *offset,
__u16 olen, u8 *data, __u16 dlen)
static int __ctc_i2c_read(struct ctc_i2c_dev *dev, __u16 chip_addr, u8 * offset,
__u16 olen, u8 * data, __u16 dlen)
{
unsigned int active = 0;
unsigned int flag = 0;
@ -371,7 +419,7 @@ static int __ctc_i2c_read(struct ctc_i2c_dev *dev, __u16 chip_addr, u8 *offset,
}
static int __ctc_i2c_write(struct ctc_i2c_dev *dev, __u16 chip_addr,
u8 *offset, __u16 olen, u8 *data, __u16 dlen)
u8 * offset, __u16 olen, u8 * data, __u16 dlen)
{
int ret;
unsigned long start_time_tx;
@ -401,8 +449,9 @@ static int __ctc_i2c_write(struct ctc_i2c_dev *dev, __u16 chip_addr,
}
data++;
start_time_tx = jiffies;
} else if (time_after(jiffies, start_time_tx +
(nb * I2C_BYTE_TO))) {
} else
if (time_after(jiffies, start_time_tx + (nb * I2C_BYTE_TO)))
{
dev_err(dev->dev, "Timed out. i2c write Failed\n");
return -ETIMEDOUT;
}
@ -418,8 +467,7 @@ static int i2c_ctc_polling_transfer(struct ctc_i2c_dev *dev)
memset(&dummy, 0, sizeof(struct i2c_msg));
/* We expect either two messages (one with an offset and one with the
* actucal data) or one message (just data)
*/
* actucal data) or one message (just data) */
if (dev->msgs_num > 2 || dev->msgs_num == 0) {
dev_err(dev->dev, "%s: Only one or two messages are supported.",
__func__);
@ -469,7 +517,7 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
u8 *buf = dev->tx_buf;
bool need_restart = false;
intr_mask = CTC_IC_INTR_DEFAULT_MASK;
intr_mask = CTC_IC_INTR_MASTER_MASK;
/* msg_write_idx */
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
@ -519,14 +567,11 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
if (rx_limit - dev->rx_outstanding <= 0)
break;
/* 1 = Read */
ctc_writel(dev, cmd | CTC_CMD_READ,
CTC_IC_DATA_CMD);
ctc_writel(dev, cmd | CTC_CMD_READ, CTC_IC_DATA_CMD); /* 1 = Read */
rx_limit--;
dev->rx_outstanding++;
} else
/* 0 = Write */
ctc_writel(dev, cmd | *buf++, CTC_IC_DATA_CMD);
ctc_writel(dev, cmd | *buf++, CTC_IC_DATA_CMD); /* 0 = Write */
tx_limit--;
buf_len--;
}
@ -538,8 +583,8 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
/* more bytes to be written */
dev->status |= STATUS_WRITE_IN_PROGRESS;
break;
}
dev->status &= ~STATUS_WRITE_IN_PROGRESS;
} else
dev->status &= ~STATUS_WRITE_IN_PROGRESS;
}
if (dev->msg_write_idx == dev->msgs_num)
@ -584,8 +629,8 @@ static void i2c_ctc_read(struct ctc_i2c_dev *dev)
dev->rx_buf_len = len;
dev->rx_buf = buf;
return;
}
dev->status &= ~STATUS_READ_IN_PROGRESS;
} else
dev->status &= ~STATUS_READ_IN_PROGRESS;
}
}
@ -627,6 +672,7 @@ static irqreturn_t i2c_ctc_isr(int this_irq, void *dev_id)
enabled = ctc_readl(dev, CTC_IC_ENABLE);
stat = ctc_readl(dev, CTC_IC_RAW_INTR_STAT);
dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled,
stat);
if (!enabled || !(stat & ~CTC_IC_INTR_ACTIVITY))
@ -658,7 +704,6 @@ tx_aborted:
static u32 i2c_ctc_func(struct i2c_adapter *adap)
{
struct ctc_i2c_dev *dev = i2c_get_adapdata(adap);
return dev->functionality;
}
@ -667,6 +712,10 @@ static struct i2c_algorithm i2c_ctc_algo = {
.functionality = i2c_ctc_func,
};
static struct i2c_bus_recovery_info i2c_ctc_recovery_info = {
.recover_bus = i2c_ctc_recover_bus,
};
int i2c_ctc_probe(struct ctc_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
@ -680,6 +729,7 @@ int i2c_ctc_probe(struct ctc_i2c_dev *dev)
snprintf(adap->name, sizeof(adap->name),
"Centec TsingMa SoC's I2C adapter");
adap->algo = &i2c_ctc_algo;
adap->bus_recovery_info = &i2c_ctc_recovery_info;
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
@ -708,6 +758,7 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
struct resource *mem;
int irq, ret;
u32 clk_freq, ht;
u32 val, i2c_num;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@ -722,6 +773,15 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
if (IS_ERR(dev->base))
return PTR_ERR(dev->base);
dev->regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(dev->regmap_base))
return PTR_ERR(dev->regmap_base);
regmap_read(dev->regmap_base,
offsetof(struct SysCtl_regs, SysCtlSysRev), &val);
dev->soc_ver = ((val == 0x1) ? CTC_REV_TM_1_1 : CTC_REV_TM_1_0);
dev->dev = &pdev->dev;
dev->irq = irq;
platform_set_drvdata(pdev, dev);
@ -738,17 +798,6 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
return -EINVAL;
}
dev->master_cfg |=
CTC_IC_CON_MASTER | CTC_IC_CON_SLAVE_DISABLE |
CTC_IC_CON_RESTART_EN;
dev->functionality =
I2C_FUNC_I2C |
I2C_FUNC_10BIT_ADDR |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
dev->clk = devm_clk_get(&pdev->dev, NULL);
clk_prepare_enable(dev->clk);
@ -757,10 +806,8 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
dev->sda_hold_time = ht / IC_ICK_NS(clk_get_rate(dev->clk));
}
if (of_property_read_bool(pdev->dev.of_node, "i2c-polling-xfer"))
dev->xfer_type = CTC_IC_POLLING_TRANSFER;
else
dev->xfer_type = CTC_IC_INTERRUPT_TRANSFER;
of_property_read_u32(pdev->dev.of_node, "i2c-num", &i2c_num);
dev->i2c_num = i2c_num;
dev->adapter.nr = pdev->id;
adap = &dev->adapter;
@ -769,6 +816,7 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
adap->dev.of_node = pdev->dev.of_node;
ret = i2c_ctc_probe(dev);
return ret;
}
@ -778,12 +826,7 @@ static int ctc_i2c_plat_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter);
/* Disable controller */
__i2c_ctc_enable(dev, false);
/* Disable all interupts */
ctc_writel(dev, 0, CTC_IC_INTR_MASK);
ctc_readl(dev, CTC_IC_CLR_INTR);
i2c_ctc_disable(dev);
return 0;
}

Просмотреть файл

@ -1,4 +1,5 @@
/* Author: Wangyb <wangyb@centecnetworks.com>
/*
* Author: Wangyb <wangyb@centecnetworks.com>
*
* Copyright 2005-2018, Centec Networks (Suzhou) Co., Ltd.
*
@ -71,7 +72,6 @@
#define CTC_IC_INTR_GEN_CALL 0x800
#define CTC_IC_INTR_DEFAULT_MASK (CTC_IC_INTR_RX_FULL | \
CTC_IC_INTR_TX_EMPTY | \
CTC_IC_INTR_TX_ABRT | \
CTC_IC_INTR_STOP_DET)
@ -112,7 +112,7 @@
CTC_IC_TX_ABRT_TXDATA_NOACK | \
CTC_IC_TX_ABRT_GCALL_NOACK)
#define CTC_CMD_READ 0x0100
#define CTC_CMD_READ 0x0100
#define CTC_STOP 0x0200
#define CTC_RESTART 0x0400
@ -125,6 +125,38 @@
#define CTC_IC_STATUS_MA 0x0020
#define CTC_IC_STATUS_TFE 0x0004
#define CTC_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
I2C_FUNC_SMBUS_BYTE | \
I2C_FUNC_SMBUS_BYTE_DATA | \
I2C_FUNC_SMBUS_WORD_DATA | \
I2C_FUNC_SMBUS_BLOCK_DATA | \
I2C_FUNC_SMBUS_I2C_BLOCK)
#define CTC_IC_INTR_MASTER_MASK (CTC_IC_INTR_DEFAULT_MASK | \
CTC_IC_INTR_TX_EMPTY)
#define CTC_IC_INTR_SLAVE_MASK (CTC_IC_INTR_DEFAULT_MASK | \
CTC_IC_INTR_RX_DONE | \
CTC_IC_INTR_RX_UNDER | \
CTC_IC_INTR_RD_REQ)
#define CTC_IC_CON_STOP_DET_IFADDRESSED 0x80
#define CTC_IC_CON_TX_EMPTY_CTRL 0x100
#define CTC_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200
#define CTC_IC_SAR 0x8
#define CTC_IC_STATUS_SLAVE_ACTIVITY BIT(6)
#define CTC_IC_BUS_CLEAR_EN 0xb0
#define CTC_IC_BUS_CLEAR_THRD 0xb4
/*
* operation modes
*/
#define CTC_IC_MASTER 0
#define CTC_IC_SLAVE 1
enum xfer_type_e {
CTC_IC_INTERRUPT_TRANSFER,
CTC_IC_POLLING_TRANSFER
@ -159,4 +191,15 @@ struct ctc_i2c_dev {
u32 clk_freq;
u32 sda_hold_time;
u32 xfer_type;
u32 mode;
u32 slave_cfg;
void (*disable) (struct ctc_i2c_dev * dev);
void (*disable_int) (struct ctc_i2c_dev * dev);
int (*init) (struct ctc_i2c_dev * dev);
struct i2c_client *slave;
struct regmap *regmap_base;
u32 soc_ver;
#define CTC_REV_TM_1_0 0x0
#define CTC_REV_TM_1_1 0x1
u32 i2c_num;
};

Просмотреть файл

@ -6,7 +6,7 @@
#define SYS_TSINGMA_TEMP_TABLE_NUM 166
#define SYS_TSINGMA_SENSOR_TIMEOUT 1000
struct ctc_switch_cmd_status_t {
typedef struct ctc_switch_cmd_status_s {
u32 cmdReadType:1;
u32 pcieReqCmdChk:3;
u32 cmdEntryWords:4;
@ -24,20 +24,20 @@ struct ctc_switch_cmd_status_t {
u32 pciePoisoned:1;
u32 regProcState:3;
u32 pcieReqOverlap:1;
};
} ctc_switch_cmd_status_t;
union ctc_switch_cmd_status_u_t {
struct ctc_switch_cmd_status_t cmd_status;
typedef union drv_pci_cmd_status_u_e {
ctc_switch_cmd_status_t cmd_status;
u32 val;
};
} ctc_switch_cmd_status_u_t;
struct ctc_access_t {
typedef struct ctc_access_s {
u32 cmd_status;
u32 addr;
u32 data[16];
};
} ctc_access_t;
extern int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value);
extern int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value);
extern int ctc5236_switch_read(u32 offset, u32 len, u32 * p_value);
extern int ctc5236_switch_write(u32 offset, u32 len, u32 * p_value);
extern int get_switch_temperature(void);
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Core private header for the pin control subsystem
*
@ -5,8 +6,6 @@
* Written on behalf of Linaro for ST-Ericsson
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/
#include <linux/kref.h>
@ -218,7 +217,7 @@ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name,
int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev,
unsigned int group_selector);
#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */
#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np);
@ -233,13 +232,12 @@ static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
}
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev,
unsigned int pin);
int pinctrl_register_map(const struct pinctrl_map *maps, unsigned num_maps,
bool dup);
void pinctrl_unregister_map(const struct pinctrl_map *map);
extern struct pinctrl_gpio_range *pinctrl_find_gpio_range_from_pin_nolock(struct
pinctrl_dev
*pctldev,
unsigned
int
pin);
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
extern int pinctrl_force_default(struct pinctrl_dev *pctldev);

Просмотреть файл

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Internal interface between the core pin control system and the
* pin config portions
@ -7,8 +8,6 @@
* Based on bits of regulator core, gpio core and clk core
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/
#ifdef CONFIG_PINCONF
@ -16,7 +15,7 @@
int pinconf_check_ops(struct pinctrl_dev *pctldev);
int pinconf_validate_map(const struct pinctrl_map *map, int i);
int pinconf_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting);
struct pinctrl_setting *setting);
void pinconf_free_setting(const struct pinctrl_setting *setting);
int pinconf_apply_setting(const struct pinctrl_setting *setting);
@ -45,7 +44,7 @@ static inline int pinconf_validate_map(const struct pinctrl_map *map, int i)
}
static inline int pinconf_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting)
struct pinctrl_setting *setting)
{
return 0;
}

Просмотреть файл

@ -8,7 +8,6 @@
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitops.h>
@ -24,6 +23,7 @@
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include "../include/sysctl.h"
#include "pinctrl-ctc.h"
#include "core.h"
@ -87,12 +87,13 @@ static void ctc_pinctrl_child_count(struct ctc_pinctrl *info,
}
static struct ctc_pin_bank *ctc_bank_num_to_bank(struct ctc_pinctrl *info,
unsigned int num)
unsigned num)
{
struct ctc_pin_bank *b = info->ctrl->pin_banks;
if (num < info->ctrl->nr_banks)
if (num < info->ctrl->nr_banks) {
return &b[num];
}
return ERR_PTR(-EINVAL);
}
@ -195,8 +196,10 @@ static int ctc_pinctrl_parse_dt(struct platform_device *pdev,
struct device_node *child;
info->ctrl = devm_kzalloc(dev, sizeof(struct ctc_pin_ctrl), GFP_KERNEL);
if (!info->ctrl)
if (!info->ctrl) {
dev_err(dev, "failed to allocate memory for pin\n");
return -EINVAL;
}
ret = of_property_read_u32(np, "ctc,pinctrl-bank0", &bank0_pins);
if (ret < 0) {
@ -229,13 +232,17 @@ static int ctc_pinctrl_parse_dt(struct platform_device *pdev,
info->functions = devm_kzalloc(dev, info->nfunctions *
sizeof(struct ctc_pmx_func), GFP_KERNEL);
if (!info->functions)
if (!info->functions) {
dev_err(dev, "failed to allocate memory for function list\n");
return -EINVAL;
}
info->groups = devm_kzalloc(dev, info->ngroups *
sizeof(struct ctc_pin_group), GFP_KERNEL);
if (!info->groups)
if (!info->groups) {
dev_err(dev, "failed allocate memory for ping group list\n");
return -EINVAL;
}
i = 0;
for_each_child_of_node(np, child) {
@ -258,7 +265,7 @@ static int ctc_get_groups_count(struct pinctrl_dev *pctldev)
}
static const char *ctc_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
unsigned selector)
{
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -266,8 +273,8 @@ static const char *ctc_get_group_name(struct pinctrl_dev *pctldev,
}
static int ctc_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector, const unsigned int **pins,
unsigned int *npins)
unsigned selector, const unsigned **pins,
unsigned *npins)
{
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -298,7 +305,7 @@ static inline const struct ctc_pin_group *ctc_pinctrl_name_to_group(const struct
static int ctc_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned int *num_maps)
struct pinctrl_map **map, unsigned *num_maps)
{
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
const struct ctc_pin_group *grp;
@ -348,7 +355,7 @@ static int ctc_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
}
static const char *ctc_pmx_get_func_name(struct pinctrl_dev *pctldev,
unsigned int selector)
unsigned selector)
{
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -356,8 +363,8 @@ static const char *ctc_pmx_get_func_name(struct pinctrl_dev *pctldev,
}
static int ctc_pmx_get_groups(struct pinctrl_dev *pctldev,
unsigned int selector, const char *const **groups,
unsigned int *const num_groups)
unsigned selector, const char *const **groups,
unsigned *const num_groups)
{
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -368,7 +375,7 @@ static int ctc_pmx_get_groups(struct pinctrl_dev *pctldev,
}
static struct ctc_pin_bank *ctc_pin_to_bank(struct ctc_pinctrl *info,
unsigned int pin)
unsigned pin)
{
struct ctc_pin_bank *b = info->ctrl->pin_banks;
@ -396,8 +403,8 @@ static int ctc_set_pin_mux(struct ctc_pinctrl *info, struct ctc_pin_bank *bank,
return 0;
}
static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
unsigned int group)
static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
const unsigned int *pins = info->groups[group].pins;
@ -408,7 +415,8 @@ static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
dev_dbg(info->dev, "enable function %s group %s\n",
info->functions[selector].name, info->groups[group].name);
/* for each pin in the pin group selected, program the corresponding pin
/*
* for each pin in the pin group selected, program the correspoding pin
* pin function number in the config register.
*/
for (cnt = 0; cnt < info->groups[group].npins; cnt++) {
@ -423,7 +431,7 @@ static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
}
static void ctc_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned int num_maps)
struct pinctrl_map *map, unsigned num_maps)
{
}
@ -528,7 +536,7 @@ static struct platform_driver ctc_pinctrl_driver = {
//static int __init ctc_pinctrl_drv_register(void)
//{
// return platform_driver_register(&ctc_pinctrl_driver);
// return platform_driver_register(&ctc_pinctrl_driver);
//}
//
//postcore_initcall(ctc_pinctrl_drv_register);

51
platform/centec-arm64/tsingma-bsp/src/pwm-ctc/pwm-ctc.c Normal file → Executable file
Просмотреть файл

@ -1,4 +1,5 @@
/* Centec PWM driver
/*
* Centec PWM driver
*
* Author: wangyb <wangyb@centecnetworks.com>
*
@ -25,14 +26,14 @@
#include <linux/mfd/syscon.h>
#include <linux/pinctrl/consumer.h>
#define CTC_NUM_PWM 4
#define CTC_CR_PWM 0x0
#define CTC_DUTY_PWM 0x4
#define CTC_NUM_PWM 4
#define CTC_CR_PWM 0x0
#define CTC_DUTY_PWM 0x4
#define CTC_MAX_PERIOD_PWM 0xFFFFFF
#define CTC_MAX_DUTY_PWM 0xFFFFFF
#define CTC_MAX_DUTY_PWM 0xFFFFFF
#define CTC_PWM_ENABLE 0x80000000
#define CTC_PWM_ENABLE 0x80000000
#define CTC_PERIOD_TACH 0x0
#define CTC_DUTY_TACH 0x4
@ -60,7 +61,6 @@ static inline u32 ctc_pwm_readl(struct ctc_pwm_chip *chip, unsigned int num,
unsigned long offset)
{
u32 val;
regmap_read(chip->regmap_base,
offsetof(struct SysCtl_regs,
SysPwmCtl) + offset + num * 0x8, &val);
@ -71,7 +71,6 @@ static inline u32 ctc_tach_readl(struct ctc_pwm_chip *chip, unsigned int num,
unsigned long offset)
{
u32 val;
regmap_read(chip->regmap_base,
offsetof(struct SysCtl_regs,
SysTachLog) + offset + num * 0x8, &val);
@ -83,19 +82,30 @@ static int ctc_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
{
struct ctc_pwm_chip *pc = to_ctc_pwm_chip(chip);
u32 cur_value;
u32 duty_cycle = 0;
u32 period_cycle = 0;
duty_ns = duty_ns / 1000;
period_ns = period_ns / 1000;
duty_cycle = duty_ns / 1000;
period_cycle = period_ns / 1000;
if (duty_cycle < 0 || period_cycle < 1)
return -1;
if (duty_cycle == 0) {
duty_cycle = 1;
} else if (duty_cycle == period_cycle) {
duty_cycle = duty_cycle - 1;
}
/* duty cycle */
duty_ns = duty_ns & CTC_MAX_DUTY_PWM;
ctc_pwm_writel(pc, pwm->hwpwm, CTC_DUTY_PWM, duty_ns);
duty_cycle = duty_cycle & CTC_MAX_DUTY_PWM;
ctc_pwm_writel(pc, pwm->hwpwm, CTC_DUTY_PWM, duty_cycle);
/* period cycle */
period_ns = period_ns & CTC_MAX_PERIOD_PWM;
period_cycle = period_cycle & CTC_MAX_PERIOD_PWM;
cur_value = ctc_pwm_readl(pc, pwm->hwpwm, CTC_CR_PWM);
cur_value &= ~(CTC_MAX_PERIOD_PWM);
cur_value |= period_ns << 0;
cur_value |= period_cycle << 0;
ctc_pwm_writel(pc, pwm->hwpwm, CTC_CR_PWM, cur_value);
return 0;
@ -139,11 +149,11 @@ static void ctc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
state->polarity = PWM_POLARITY_NORMAL;
state->period = (cur_value & (~CTC_PWM_ENABLE)) * 1000;
state->period = (cur_value & (~CTC_PWM_ENABLE)) * 1000; // in nanoseconds
cur_value_2 = ctc_pwm_readl(pc, pwm->hwpwm, CTC_DUTY_PWM);
state->duty_cycle = cur_value_2 * 1000;
state->duty_cycle = cur_value_2 * 1000; // in nanoseconds
}
static int ctc_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
@ -154,10 +164,10 @@ static int ctc_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
u32 duty_tach;
period_tach = ctc_tach_readl(pc, pwm->hwpwm, CTC_PERIOD_TACH) / 4;
result->period = period_tach * 1000;
result->period = period_tach * 1000; // in nanoseconds
duty_tach = ctc_tach_readl(pc, pwm->hwpwm, CTC_DUTY_TACH) / 4;
result->duty_cycle = duty_tach * 1000;
result->duty_cycle = duty_tach * 1000; // in nanoseconds
return 0;
}
@ -188,12 +198,13 @@ static int ctc_pwm_probe(struct platform_device *pdev)
pc->regmap_base = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"ctc,sysctrl");
pctldev = devm_kzalloc(&pdev->dev, sizeof(*pctldev), GFP_KERNEL);
if (!pctldev)
if (!pctldev) {
return -1;
}
pctldev->p = pinctrl_get(&pdev->dev);
state = pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
pinctrl_select_state(pctldev->p, state);
pr_info("Select PWM Function\n");
printk("Select PWM Function\n");
for (i = 0; i < CTC_NUM_PWM; i++) {
cur_value = ctc_pwm_readl(pc, i, CTC_CR_PWM);

Просмотреть файл

@ -1,4 +1,5 @@
/* rtc class driver for the SD2405 chip
/*
* rtc class driver for the SD2405 chip
*
* Author: Dale Farnsworth <dale@farnsworth.org>
*
@ -45,7 +46,7 @@
static struct i2c_driver sd2405_driver;
static int sd2405_i2c_read_regs(struct i2c_client *client, u8 *buf)
static int sd2405_i2c_read_regs(struct i2c_client *client, u8 * buf)
{
struct i2c_msg msgs[1] = {
{
@ -67,7 +68,9 @@ static int sd2405_i2c_read_regs(struct i2c_client *client, u8 *buf)
static int sd2405_i2c_write_regs(struct i2c_client *client, u8 const *buf)
{
int rc;
u8 temp_reg[SD2405_REG_LEN + 1] = { 0 };
memcpy(&temp_reg[1], buf, SD2405_REG_LEN);
struct i2c_msg msgs[1] = {
{
@ -77,8 +80,6 @@ static int sd2405_i2c_write_regs(struct i2c_client *client, u8 const *buf)
.buf = temp_reg}
};
memcpy(&temp_reg[1], buf, SD2405_REG_LEN);
rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (rc != ARRAY_SIZE(msgs))
goto write_failed;
@ -114,7 +115,6 @@ static int sd2405_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
static int sd2405_i2c_set_write_protect(struct i2c_client *client)
{
int rc;
rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL1, 0);
rc += i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2, 0);
if (rc < 0) {
@ -128,7 +128,6 @@ static int sd2405_i2c_set_write_protect(struct i2c_client *client)
static int sd2405_i2c_clear_write_protect(struct i2c_client *client)
{
int rc;
rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2,
SD2405_REG_CONTROL1_WRITE);
rc +=
@ -183,11 +182,12 @@ static int sd2405_rtc_set_time(struct device *dev, struct rtc_time *tm)
static int sd2405_remove(struct i2c_client *client)
{
#if 0
struct rtc_device *rtc = i2c_get_clientdata(client);
if (rtc)
rtc_device_unregister(rtc);
#endif
return 0;
}
@ -199,17 +199,20 @@ static const struct rtc_class_ops sd2405_rtc_ops = {
static int
sd2405_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct rtc_device *rtc;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
rtc = rtc_device_register(sd2405_driver.driver.name,
&client->dev, &sd2405_rtc_ops, THIS_MODULE);
rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
rtc->ops = &sd2405_rtc_ops;
ret = rtc_register_device(rtc);
if (ret)
return ret;
i2c_set_clientdata(client, rtc);

Просмотреть файл

@ -1,17 +1,9 @@
/* sdhci-ctc5236.c Support for SDHCI on Centec TsingMa SoC's
/*
* sdhci-ctc5236.c Support for SDHCI on Centec TsingMa SoC's
*
* Copyright (C) 2004-2017 Centec Networks (suzhou) Co., LTD.
* Author: Wangyb <wangyb@centecnetworks.com>
*
* Author: Jay Cao <caoj@centecnetworks.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.
*
* 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.
* Copyright 2005-2020, Centec Networks (Suzhou) Co., Ltd.
*
*/
@ -27,38 +19,43 @@
#include <asm-generic/delay.h>
#include <linux/delay.h>
#include <linux/raid/pq.h>
#include <linux/sizes.h>
#include <linux/dma-mapping.h>
#define REG_OFFSET_ADDR 0x500
#define MSHC_CTRL_R 0x8
#define AT_CTRL_R 0x40
#define SW_TUNE_EN 0x10
#define SD_CLK_EN_MASK 0x00000001
#define SD_CLK_EN_MASK 0x00000001
#define AT_STAT_R 0x44
#define MAX_TUNING_LOOP 0x80
#define MIN_TUNING_LOOP 0x0
#define MIN_TUNING_LOOP 0x0
#define TUNE_CTRL_STEP 1
#define EMMC_CTRL_R 0x2c
struct regmap *regmap_base;
#define SDHCI_REFCLK_150M 150000000
static struct regmap *regmap_base;
static u32 version;
#define CTC_REV_TM_1_0 0x0
#define CTC_REV_TM_1_1 0x1
#define BOUNDARY_OK(addr, len) \
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
static u16 sdhci_ctc5236_readw(struct sdhci_host *host, int reg)
{
if (unlikely(reg == SDHCI_HOST_VERSION))
if (unlikely(reg == SDHCI_HOST_VERSION)) {
return SDHCI_SPEC_300;
}
return readw(host->ioaddr + reg);
}
static u32 sdhci_ctc5236_readl(struct sdhci_host *host, int reg)
{
u32 ret = readl(host->ioaddr + reg);
if (reg == SDHCI_CAPABILITIES_1)
ret &=
~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
if (reg == SDHCI_CAPABILITIES)
ret &= ~(SDHCI_CAN_64BIT);
u32 ret;
ret = readl(host->ioaddr + reg);
return ret;
}
@ -112,24 +109,74 @@ void sdhci_ctc5236_reset(struct sdhci_host *host, u8 mask)
}
static void ctc5236_select_90degree_phase(struct sdhci_host *host)
{
u32 val = 0;
regmap_read(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), &val);
if (val & SYS_MSH_CFG_W0_MSH_INTF_C_CLK_TX_PHASE_SEL_MASK) {
val &= (~SYS_MSH_CFG_W0_MSH_INTF_C_CLK_TX_PHASE_SEL_MASK);
regmap_write(regmap_base,
offsetof(struct SysCtl_regs, SysMshCfg), val);
printk("select ctc 90 degree phase\n");
}
}
void ctc_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
int val = 0;
if (clock == SDHCI_REFCLK_150M) {
/* SDHCI reference clock change 150M */
regmap_read(regmap_base,
offsetof(struct SysCtl_regs, SysClkPeriCfg), &val);
val = val & (~SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK);
val |=
((0x8 & SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK)) << 0;
regmap_write(regmap_base,
offsetof(struct SysCtl_regs, SysClkPeriCfg), val);
if ((val & 0xc) == 0xc) {
val =
val &
(~SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK);
val |=
((0x8 &
SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK)) <<
0;
regmap_write(regmap_base,
offsetof(struct SysCtl_regs,
SysClkPeriCfg), val);
printk("SDHCI reference clock change 150M\n");
}
}
if (version == CTC_REV_TM_1_1) {
if (host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) {
ctc5236_select_90degree_phase(host);
}
}
sdhci_set_clock(host, clock);
}
/*
* If DMA addr spans 128MB boundary, we split the DMA transfer into two
* so that each DMA transfer doesn't exceed the boundary.
*/
static void sdhci_ctc5236_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len,
unsigned int cmd)
{
int tmplen, offset;
if (likely(!len || BOUNDARY_OK(addr, len))) {
sdhci_adma_write_desc(host, desc, addr, len, cmd);
return;
}
offset = addr & (SZ_128M - 1);
tmplen = SZ_128M - offset;
sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
addr += tmplen;
len -= tmplen;
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
static int sdhci_ctc5236_prepare_tuning(struct sdhci_host *host,
int CENTER_PH_CODE)
{
@ -180,51 +227,54 @@ static int sdhci_ctc5236_execute_tuning(struct sdhci_host *host, u32 opcode)
val &= (~SYS_MSH_CFG_W0_MSH_INTF_RX_DLL_MASTER_BYPASS_MASK);
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), val);
sdhci_ctc5236_prepare_tuning(host, 0);
/* find the mininum delay first which can pass tuning */
min = MIN_TUNING_LOOP;
sdhci_ctc5236_prepare_tuning(host, min);
sdhci_writel(host, min, REG_OFFSET_ADDR + AT_STAT_R);
while (min < MAX_TUNING_LOOP) {
dev_dbg(mmc_dev(host->mmc), "#1# AT_STAT_R is %x\n",
sdhci_readl(host, REG_OFFSET_ADDR + AT_STAT_R));
if (!mmc_send_tuning(host->mmc, opcode, NULL))
break;
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
min += TUNE_CTRL_STEP;
sdhci_writel(host, min, REG_OFFSET_ADDR + AT_STAT_R);
}
/* find the maxinum delay which can not pass tuning */
max = min + TUNE_CTRL_STEP;
sdhci_ctc5236_prepare_tuning(host, max);
sdhci_writel(host, max, REG_OFFSET_ADDR + AT_STAT_R);
while (max < MAX_TUNING_LOOP) {
dev_dbg(mmc_dev(host->mmc), "#2# AT_STAT_R is %x\n",
sdhci_readl(host, REG_OFFSET_ADDR + AT_STAT_R));
if (mmc_send_tuning(host->mmc, opcode, NULL)) {
max -= TUNE_CTRL_STEP;
break;
}
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
max += TUNE_CTRL_STEP;
sdhci_writel(host, max, REG_OFFSET_ADDR + AT_STAT_R);
}
/* use average delay to get the best timing */
avg = (min + max) / 2;
sdhci_ctc5236_prepare_tuning(host, avg);
sdhci_writel(host, avg, REG_OFFSET_ADDR + AT_STAT_R);
ret = mmc_send_tuning(host->mmc, opcode, NULL);
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
sdhci_writel(host, avg, REG_OFFSET_ADDR + AT_STAT_R);
dev_info(mmc_dev(host->mmc), "Tuning %s at 0x%x ret %d\n",
ret ? "failed" : "passed", avg, ret);
dev_info(mmc_dev(host->mmc),
"Tuning %s at 0x%x ret %d, min is 0x%x, max is 0x%x\n",
ret ? "failed" : "passed", avg, ret, min, max);
return ret;
}
static void sdhci_ctc5236_hw_reset(struct sdhci_host *host)
{
sdhci_writel(host, 0x0, REG_OFFSET_ADDR + EMMC_CTRL_R);
udelay(10);
sdhci_writel(host, 0xc, REG_OFFSET_ADDR + EMMC_CTRL_R);
udelay(300);
dev_info(mmc_dev(host->mmc), "Hardware reset\n");
}
static const struct sdhci_ops sdhci_ctc5236_ops = {
.read_w = sdhci_ctc5236_readw,
.read_l = sdhci_ctc5236_readl,
@ -234,12 +284,14 @@ static const struct sdhci_ops sdhci_ctc5236_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.platform_execute_tuning = sdhci_ctc5236_execute_tuning,
.adma_write_desc = sdhci_ctc5236_adma_write_desc,
.hw_reset = sdhci_ctc5236_hw_reset,
};
static struct sdhci_pltfm_data sdhci_ctc5236_pdata = {
.ops = &sdhci_ctc5236_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_BROKEN_HS200,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_BROKEN_ADMA,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
};
static int sdhci_ctc5236_probe(struct platform_device *pdev)
@ -247,12 +299,40 @@ static int sdhci_ctc5236_probe(struct platform_device *pdev)
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct clk *clk;
int ret;
int ret, val;
u32 extra;
host = sdhci_pltfm_init(pdev, &sdhci_ctc5236_pdata, 0);
if (IS_ERR(host))
return PTR_ERR(host);
/*
* extra adma table cnt for cross 128M boundary handling.
*/
extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M);
if (extra > SDHCI_MAX_SEGS)
extra = SDHCI_MAX_SEGS;
host->adma_table_cnt += extra;
regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(regmap_base))
return PTR_ERR(regmap_base);
val = 0x3400027;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), val);
regmap_read(regmap_base, offsetof(struct SysCtl_regs, SysCtlSysRev),
&val);
version = (val == 0x1) ? CTC_REV_TM_1_1 : CTC_REV_TM_1_0;
mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
ret = mmc_of_parse(host->mmc);
if (ret)
goto err_sdhci_add;
clk = devm_clk_get(&pdev->dev, "mmc_clk");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Peripheral clk not found\n");
@ -262,16 +342,17 @@ static int sdhci_ctc5236_probe(struct platform_device *pdev)
pltfm_host->clk = clk;
clk_prepare_enable(clk);
regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(regmap_base))
return PTR_ERR(regmap_base);
if (version == CTC_REV_TM_1_0) {
if (host->mmc->caps & MMC_CAP_1_8V_DDR) {
host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
printk("%s, not support DDR Mode\n", __func__);
}
}
mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
ret = mmc_of_parse(host->mmc);
if (ret)
goto err_sdhci_add;
if (host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) {
host->mmc->caps2 &= ~MMC_CAP2_HS200_1_8V_SDR;
printk("%s, not support Hs200 Mode\n", __func__);
}
ret = sdhci_add_host(host);
if (ret)
@ -303,5 +384,5 @@ static struct platform_driver sdhci_ctc5236_driver = {
module_platform_driver(sdhci_ctc5236_driver);
MODULE_DESCRIPTION("SDHCI driver for Centec TsingMa SoCs");
MODULE_AUTHOR("Jay Cao <caoj@centecnetworks.com>");
MODULE_AUTHOR("Wangyb <wangyb@centecnetworks.com>");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -1,11 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2010 MontaVista Software, LLC.
*
* Author: Anton Vorontsov <avorontsov@ru.mvista.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.
*/
#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
@ -28,7 +25,7 @@ struct sdhci_pltfm_host {
unsigned int clock;
u16 xfer_mode_shadow;
unsigned long private[0] ____cacheline_aligned;
unsigned long private[] ____cacheline_aligned;
};
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
@ -86,15 +83,20 @@ static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
int base = reg & ~0x3;
int shift = (reg & 0x3) * 8;
clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
clrsetbits_be32(host->ioaddr + base, 0xff << shift, val << shift);
}
#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
extern void sdhci_get_of_property(struct platform_device *pdev);
void sdhci_get_property(struct platform_device *pdev);
static inline void sdhci_get_of_property(struct platform_device *pdev)
{
return sdhci_get_property(pdev);
}
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size);
const struct sdhci_pltfm_data *pdata,
size_t priv_size);
extern void sdhci_pltfm_free(struct platform_device *pdev);
extern int sdhci_pltfm_register(struct platform_device *pdev,
@ -109,8 +111,20 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
return host->private;
}
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#ifdef CONFIG_PM_SLEEP
int sdhci_pltfm_suspend(struct device *dev);
int sdhci_pltfm_resume(struct device *dev);
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#else
static inline int sdhci_pltfm_suspend(struct device *dev)
{
return 0;
}
static inline int sdhci_pltfm_resume(struct device *dev)
{
return 0;
}
#endif
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */

Просмотреть файл

@ -1,18 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
*
* Header file for Host Controller registers and I/O accessors.
*
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef __SDHCI_HW_H
#define __SDHCI_HW_H
#include <linux/bits.h>
#include <linux/scatterlist.h>
#include <linux/compiler.h>
#include <linux/types.h>
@ -28,6 +25,7 @@
#define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
#define SDHCI_32BIT_BLK_CNT SDHCI_DMA_ADDRESS
#define SDHCI_BLOCK_SIZE 0x04
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
@ -41,6 +39,7 @@
#define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_AUTO_CMD12 0x04
#define SDHCI_TRNS_AUTO_CMD23 0x08
#define SDHCI_TRNS_AUTO_SEL 0x0C
#define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20
@ -71,6 +70,10 @@
#define SDHCI_SPACE_AVAILABLE 0x00000400
#define SDHCI_DATA_AVAILABLE 0x00000800
#define SDHCI_CARD_PRESENT 0x00010000
#define SDHCI_CARD_PRES_SHIFT 16
#define SDHCI_CD_STABLE 0x00020000
#define SDHCI_CD_LVL 0x00040000
#define SDHCI_CD_LVL_SHIFT 18
#define SDHCI_WRITE_PROTECT 0x00080000
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
@ -86,7 +89,8 @@
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_CTRL_ADMA3 0x18
#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_CTRL_CDTEST_INS 0x40
#define SDHCI_CTRL_CDTEST_EN 0x80
@ -111,6 +115,7 @@
#define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
#define SDHCI_CLOCK_CARD_EN 0x0004
#define SDHCI_CLOCK_PLL_EN 0x0008
#define SDHCI_CLOCK_INT_STABLE 0x0002
#define SDHCI_CLOCK_INT_EN 0x0001
@ -180,7 +185,7 @@
#define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
@ -189,15 +194,16 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
#define SDHCI_CMD23_ENABLE 0x0800
#define SDHCI_CTRL_V4_MODE 0x1000
#define SDHCI_CTRL_64BIT_ADDR 0x2000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
#define SDHCI_TIMEOUT_CLK_SHIFT 0
#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
#define SDHCI_CLOCK_BASE_SHIFT 8
#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
#define SDHCI_MAX_BLOCK_MASK 0x00030000
#define SDHCI_MAX_BLOCK_SHIFT 16
#define SDHCI_CAN_DO_8BIT 0x00040000
@ -209,33 +215,28 @@
#define SDHCI_CAN_VDD_330 0x01000000
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT_V4 0x08000000
#define SDHCI_CAN_64BIT 0x10000000
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
#define SDHCI_USE_SDR50_TUNING 0x00002000
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
#define SDHCI_CAN_DO_ADMA3 0x08000000
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_MAX_CURRENT 0x48
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
#define SDHCI_MAX_CURRENT_330_SHIFT 0
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
#define SDHCI_MAX_CURRENT_300_SHIFT 8
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
#define SDHCI_MAX_CURRENT_180_SHIFT 16
#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */
@ -252,18 +253,16 @@
/* 60-FB reserved */
#define SDHCI_PRESET_FOR_HIGH_SPEED 0x64
#define SDHCI_PRESET_FOR_SDR12 0x66
#define SDHCI_PRESET_FOR_SDR25 0x68
#define SDHCI_PRESET_FOR_SDR50 0x6A
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
#define SDHCI_PRESET_DRV_MASK 0xC000
#define SDHCI_PRESET_DRV_SHIFT 14
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10
#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
#define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
#define SDHCI_PRESET_CLKGEN_SEL BIT(10)
#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
#define SDHCI_SLOT_INT_STATUS 0xFC
@ -275,6 +274,9 @@
#define SDHCI_SPEC_100 0
#define SDHCI_SPEC_200 1
#define SDHCI_SPEC_300 2
#define SDHCI_SPEC_400 3
#define SDHCI_SPEC_410 4
#define SDHCI_SPEC_420 5
/*
* End of controller registers.
@ -294,10 +296,10 @@
/* ADMA2 32-bit descriptor */
struct sdhci_adma2_32_desc {
__le16 cmd;
__le16 len;
__le32 addr;
} __packed __aligned(4);
__le16 cmd;
__le16 len;
__le32 addr;
} __packed __aligned(4);
/* ADMA2 data alignment */
#define SDHCI_ADMA2_ALIGN 4
@ -310,19 +312,25 @@ struct sdhci_adma2_32_desc {
*/
#define SDHCI_ADMA2_DESC_ALIGN 8
/* ADMA2 64-bit DMA descriptor size */
#define SDHCI_ADMA2_64_DESC_SZ 12
/*
* ADMA2 64-bit DMA descriptor size
* According to SD Host Controller spec v4.10, there are two kinds of
* descriptors for 64-bit addressing mode: 96-bit Descriptor and 128-bit
* Descriptor, if Host Version 4 Enable is set in the Host Control 2
* register, 128-bit Descriptor will be selected.
*/
#define SDHCI_ADMA2_64_DESC_SZ(host) ((host)->v4_mode ? 16 : 12)
/*
* ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
* aligned.
*/
struct sdhci_adma2_64_desc {
__le16 cmd;
__le16 len;
__le32 addr_lo;
__le32 addr_hi;
} __packed __aligned(4);
__le16 cmd;
__le16 len;
__le32 addr_lo;
__le32 addr_hi;
} __packed __aligned(4);
#define ADMA2_TRAN_VALID 0x21
#define ADMA2_NOP_END_VALID 0x3
@ -343,7 +351,7 @@ struct sdhci_adma2_64_desc {
* command and response, and the time between response and start of data is
* not known, set the command transfer time to 10ms.
*/
#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
enum sdhci_cookie {
COOKIE_UNMAPPED,
@ -391,8 +399,12 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
/* Controller reports inverted write-protect state */
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
/* Controller has unusable command queue engine */
#define SDHCI_QUIRK_BROKEN_CQE (1<<17)
/* Controller does not like fast PIO transfers */
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
/* Controller does not have a LED */
#define SDHCI_QUIRK_NO_LED (1<<19)
/* Controller has to be forced to use block size of 2048 bytes */
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
/* Controller cannot do multi-block transfers */
@ -455,9 +467,17 @@ struct sdhci_host {
* obtainable timeout.
*/
#define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1<<17)
/*
* 32-bit block count may not support eMMC where upper bits of CMD23 are used
* for other purposes. Consequently we support 16-bit block count by default.
* Otherwise, SDHCI_QUIRK2_USE_32BIT_BLK_CNT can be selected to use 32-bit
* block count.
*/
#define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
phys_addr_t mapbase; /* physical address base */
char *bounce_buffer; /* For packing SDMA reads/writes */
dma_addr_t bounce_addr;
unsigned int bounce_buffer_size;
@ -485,7 +505,6 @@ struct sdhci_host {
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
@ -506,10 +525,14 @@ struct sdhci_host {
bool preset_enabled; /* Preset is enabled */
bool pending_reset; /* Cmd/data reset is pending */
bool irq_wake_enabled; /* IRQ wakeup is enabled */
bool v4_mode; /* Host Version 4 Enable */
bool use_external_dma; /* Host selects to use external DMA */
bool always_defer_done; /* Always defer to complete requests */
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
struct mmc_command *cmd; /* Current command */
struct mmc_command *data_cmd; /* Current data command */
struct mmc_command *deferred_cmd; /* Deferred command */
struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */
@ -527,87 +550,109 @@ struct sdhci_host {
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
dma_addr_t align_addr; /* Mapped bounce buffer */
unsigned int desc_sz; /* ADMA descriptor size */
unsigned int desc_sz; /* ADMA current descriptor size */
unsigned int alloc_desc_sz; /* ADMA descr. max size host supports */
struct tasklet_struct finish_tasklet; /* Tasklet structures */
struct workqueue_struct *complete_wq; /* Request completion wq */
struct work_struct complete_work; /* Request completion work */
struct timer_list timer; /* Timer for timeouts */
struct timer_list data_timer; /* Timer for data timeouts */
#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
struct dma_chan *rx_chan;
struct dma_chan *tx_chan;
#endif
u32 caps; /* CAPABILITY_0 */
u32 caps1; /* CAPABILITY_1 */
bool read_caps; /* Capability flags have been read */
unsigned int ocr_avail_sdio; /* OCR bit masks */
unsigned int ocr_avail_sd;
unsigned int ocr_avail_mmc;
bool sdhci_core_to_disable_vqmmc; /* sdhci core can disable vqmmc */
unsigned int ocr_avail_sdio; /* OCR bit masks */
unsigned int ocr_avail_sd;
unsigned int ocr_avail_mmc;
u32 ocr_mask; /* available voltages */
unsigned timing; /* Current timing */
unsigned timing; /* Current timing */
u32 thread_isr;
u32 thread_isr;
/* cached registers */
u32 ier;
u32 ier;
bool cqe_on; /* CQE is operating */
u32 cqe_ier; /* CQE interrupt mask */
u32 cqe_err_ier; /* CQE error interrupt mask */
bool cqe_on; /* CQE is operating */
u32 cqe_ier; /* CQE interrupt mask */
u32 cqe_err_ier; /* CQE error interrupt mask */
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
unsigned int tuning_count; /* Timer count for re-tuning */
unsigned int tuning_mode; /* Re-tuning mode supported by host */
unsigned int tuning_count; /* Timer count for re-tuning */
unsigned int tuning_mode; /* Re-tuning mode supported by host */
unsigned int tuning_err; /* Error code for re-tuning */
#define SDHCI_TUNING_MODE_1 0
#define SDHCI_TUNING_MODE_2 1
#define SDHCI_TUNING_MODE_3 2
/* Delay (ms) between tuning commands */
int tuning_delay;
int tuning_delay;
int tuning_loop_count;
/* Host SDMA buffer boundary. */
u32 sdma_boundary;
u32 sdma_boundary;
u64 data_timeout;
/* Host ADMA table count */
u32 adma_table_cnt;
unsigned long private[0] ____cacheline_aligned;
u64 data_timeout;
unsigned long private[] ____cacheline_aligned;
};
struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg);
u16 (*read_w)(struct sdhci_host *host, int reg);
u8 (*read_b)(struct sdhci_host *host, int reg);
void (*write_l)(struct sdhci_host *host, u32 val, int reg);
void (*write_w)(struct sdhci_host *host, u16 val, int reg);
void (*write_b)(struct sdhci_host *host, u8 val, int reg);
u32(*read_l) (struct sdhci_host * host, int reg);
u16(*read_w) (struct sdhci_host * host, int reg);
u8(*read_b) (struct sdhci_host * host, int reg);
void (*write_l) (struct sdhci_host * host, u32 val, int reg);
void (*write_w) (struct sdhci_host * host, u16 val, int reg);
void (*write_b) (struct sdhci_host * host, u8 val, int reg);
#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
void (*set_power)(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void (*set_clock) (struct sdhci_host * host, unsigned int clock);
void (*set_power) (struct sdhci_host * host, unsigned char mode,
unsigned short vdd);
u32 (*irq)(struct sdhci_host *host, u32 intmask);
u32(*irq) (struct sdhci_host * host, u32 intmask);
int (*enable_dma)(struct sdhci_host *host);
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host);
int (*set_dma_mask) (struct sdhci_host * host);
int (*enable_dma) (struct sdhci_host * host);
unsigned int (*get_max_clock) (struct sdhci_host * host);
unsigned int (*get_min_clock) (struct sdhci_host * host);
/* get_timeout_clock should return clk rate in unit of Hz */
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
unsigned int (*get_max_timeout_count)(struct sdhci_host *host);
void (*set_timeout)(struct sdhci_host *host,
struct mmc_command *cmd);
void (*set_bus_width)(struct sdhci_host *host, int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
void (*reset)(struct sdhci_host *host, u8 mask);
int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
void (*card_event)(struct sdhci_host *host);
void (*voltage_switch)(struct sdhci_host *host);
unsigned int (*get_timeout_clock) (struct sdhci_host * host);
unsigned int (*get_max_timeout_count) (struct sdhci_host * host);
void (*set_timeout) (struct sdhci_host * host,
struct mmc_command * cmd);
void (*set_bus_width) (struct sdhci_host * host, int width);
void (*platform_send_init_74_clocks) (struct sdhci_host * host,
u8 power_mode);
unsigned int (*get_ro) (struct sdhci_host * host);
void (*reset) (struct sdhci_host * host, u8 mask);
int (*platform_execute_tuning) (struct sdhci_host * host, u32 opcode);
void (*set_uhs_signaling) (struct sdhci_host * host, unsigned int uhs);
void (*hw_reset) (struct sdhci_host * host);
void (*adma_workaround) (struct sdhci_host * host, u32 intmask);
void (*card_event) (struct sdhci_host * host);
void (*voltage_switch) (struct sdhci_host * host);
void (*adma_write_desc) (struct sdhci_host * host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
void (*copy_to_bounce_buffer) (struct sdhci_host * host,
struct mmc_data * data,
unsigned int length);
void (*request_done) (struct sdhci_host * host,
struct mmc_request * mrq);
void (*dump_vendor_regs) (struct sdhci_host * host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@ -703,33 +748,31 @@ static inline void *sdhci_priv(struct sdhci_host *host)
}
void sdhci_card_detect(struct sdhci_host *host);
void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps,
u32 *caps1);
void __sdhci_read_caps(struct sdhci_host *host, const u16 * ver,
const u32 * caps, const u32 * caps1);
int sdhci_setup_host(struct sdhci_host *host);
void sdhci_cleanup_host(struct sdhci_host *host);
int __sdhci_add_host(struct sdhci_host *host);
int sdhci_add_host(struct sdhci_host *host);
void sdhci_remove_host(struct sdhci_host *host, int dead);
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
static inline void sdhci_read_caps(struct sdhci_host *host)
{
__sdhci_read_caps(host, NULL, NULL, NULL);
}
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
{
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
}
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock);
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
void sdhci_enable_clk(struct sdhci_host *host, u16 clk);
void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
unsigned char mode, unsigned short vdd);
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
@ -738,12 +781,14 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios);
void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
#ifdef CONFIG_PM
int sdhci_suspend_host(struct sdhci_host *host);
int sdhci_resume_host(struct sdhci_host *host);
int sdhci_runtime_suspend_host(struct sdhci_host *host);
int sdhci_runtime_resume_host(struct sdhci_host *host);
int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset);
#endif
void sdhci_cqe_enable(struct mmc_host *mmc);
@ -752,10 +797,15 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
int *data_error);
void sdhci_dumpregs(struct sdhci_host *host);
void sdhci_enable_v4_mode(struct sdhci_host *host);
void sdhci_start_tuning(struct sdhci_host *host);
void sdhci_end_tuning(struct sdhci_host *host);
void sdhci_reset_tuning(struct sdhci_host *host);
void sdhci_send_tuning(struct sdhci_host *host, u32 opcode);
void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode);
void sdhci_switch_external_dma(struct sdhci_host *host, bool en);
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable);
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
#endif /* __SDHCI_HW_H */

Просмотреть файл

@ -111,7 +111,7 @@ enum type_mode {
TYPE_MAX
};
static int ctc_reg_read(struct ctc_qspi *ctc_qspi, u32 reg, u32 *value)
static int ctc_reg_read(struct ctc_qspi *ctc_qspi, u32 reg, u32 * value)
{
*value = readl(ctc_qspi->regs + reg);
return *value;
@ -169,12 +169,12 @@ static noinline int ctc_write_tx_buf(struct ctc_qspi *ctc_qspi, u8 offset,
return 0;
}
static noinline int check_buf_ok(u8 *buf, int i)
static noinline int check_buf_ok(u8 * buf, int i)
{
return buf && (buf + i);
}
static noinline int fill_tx_entry(struct ctc_qspi *ctc_qspi, u8 *buf, int i,
static noinline int fill_tx_entry(struct ctc_qspi *ctc_qspi, u8 * buf, int i,
u8 off)
{
ctc_qspi->tx_entry |= buf[i] << (off % 4) * 8;
@ -182,12 +182,12 @@ static noinline int fill_tx_entry(struct ctc_qspi *ctc_qspi, u8 *buf, int i,
return 0;
}
static noinline void update_offset(u8 *offset, u8 off)
static noinline void update_offset(u8 * offset, u8 off)
{
*offset = off;
}
static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf,
static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 * offset, u8 * buf,
u32 len)
{
@ -195,8 +195,9 @@ static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf,
u8 off = *offset;
while (i < len) {
if (check_buf_ok(buf, i))
if (check_buf_ok(buf, i)) {
fill_tx_entry(ctc_qspi, buf, i, off);
}
if (off % 4 == 0) {
ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry);
@ -210,7 +211,7 @@ static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf,
}
static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 *offset, u8 *buf,
static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 * offset, u8 * buf,
u32 len)
{
u32 i = 0, j = 0;
@ -218,8 +219,9 @@ static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 *offset, u8 *buf,
while (i < len) {
for (j = 0; j < 4; j++) {
if (buf && (buf + i))
if (buf && (buf + i)) {
ctc_qspi->tx_entry |= buf[i + j] << (j % 4) * 8;
}
}
ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry);
ctc_qspi->tx_entry = 0;
@ -303,15 +305,17 @@ static void ctc_qspi_pio_ctrl(struct ctc_qspi *ctc_qspi)
{
u32 ctrl = 0;
ctrl = CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) |
CTRL_PRE_CYCLE(ctc_qspi->precycle) |
CTRL_POST_CYCLE(ctc_qspi->postcycle) |
CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef) |
CTRL_SOUT3_DEFAULT(ctc_qspi->sout3def) |
CTRL_SOUT2_DEFAULT(ctc_qspi->sout2def) |
CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) |
CTRL_CS(ctc_qspi->cs_select) |
CTRL_DIV_SCLK(ctc_qspi->clkdiv);
ctrl =
CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | CTRL_PRE_CYCLE(ctc_qspi->
precycle)
| CTRL_POST_CYCLE(ctc_qspi->
postcycle) | CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef)
| CTRL_SOUT3_DEFAULT(ctc_qspi->
sout3def) | CTRL_SOUT2_DEFAULT(ctc_qspi->
sout2def)
| CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) | CTRL_CS(ctc_qspi->
cs_select)
| CTRL_DIV_SCLK(ctc_qspi->clkdiv);
ctc_reg_write_mask(ctc_qspi, PIO_CTRL(ctc_qspi->qspi_mode), ctrl,
0xffffffff);
@ -321,15 +325,17 @@ static void ctc_qspi_pp_ctrl(struct ctc_qspi *ctc_qspi)
{
u32 ctrl = 0;
ctrl = CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) |
CTRL_PRE_CYCLE(ctc_qspi->precycle) |
CTRL_POST_CYCLE(ctc_qspi->postcycle) |
CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef) |
CTRL_SOUT3_DEFAULT(ctc_qspi->sout3def) |
CTRL_SOUT2_DEFAULT(ctc_qspi->sout2def) |
CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) |
CTRL_CS(ctc_qspi->cs_select) |
CTRL_DIV_SCLK(ctc_qspi->clkdiv);
ctrl =
CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | CTRL_PRE_CYCLE(ctc_qspi->
precycle)
| CTRL_POST_CYCLE(ctc_qspi->
postcycle) | CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef)
| CTRL_SOUT3_DEFAULT(ctc_qspi->
sout3def) | CTRL_SOUT2_DEFAULT(ctc_qspi->
sout2def)
| CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) | CTRL_CS(ctc_qspi->
cs_select)
| CTRL_DIV_SCLK(ctc_qspi->clkdiv);
ctc_reg_write_mask(ctc_qspi, PP_CTRL, ctrl, 0xffffffff);
}
@ -343,18 +349,17 @@ static u32 ctc_pp_conf(u8 lanes, u32 len)
return (lanes << 16) | (cycle);
}
static int ctc_read_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 *value)
static int ctc_read_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 * value)
{
*value = readb(ctc_qspi->regs + CTC_QSPI_RX_BUFF + offset);
return 0;
}
static void ctc_extra_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 *buf,
static void ctc_extra_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 * buf,
u8 len)
{
int i = 0;
while (i < len) {
ctc_read_rx_buf(ctc_qspi, offset, &buf[i++]);
offset--;
@ -523,11 +528,13 @@ int ctc_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
//max data transfer size = tx buffer size - (cmd - addr -dummy )
if (op->data.dir == SPI_MEM_DATA_IN) {
if (op->data.nbytes > CTC_QSPI_RX_BUFFER_SIZE - 6)
if (op->data.nbytes > CTC_QSPI_RX_BUFFER_SIZE - 6) {
op->data.nbytes = CTC_QSPI_RX_BUFFER_SIZE - 6;
}
} else {
if (op->data.nbytes > CTC_QSPI_TX_BUFFER_SIZE)
if (op->data.nbytes > CTC_QSPI_TX_BUFFER_SIZE) {
op->data.nbytes = CTC_QSPI_TX_BUFFER_SIZE;
}
}
return 0;
@ -558,7 +565,7 @@ static int ctc_qspi_probe(struct platform_device *pdev)
return -ENOMEM;
master->mode_bits =
SPI_MODE_3 | SPI_MODE_1 | SPI_TX_DUAL | SPI_RX_DUAL | SPI_TX_QUAD |
SPI_MODE_3 | SPI_MODE_0 | SPI_TX_DUAL | SPI_RX_DUAL | SPI_TX_QUAD |
SPI_RX_QUAD;
master->setup = ctc_qspi_setup;
master->transfer_one_message = ctc_qspi_start_transfer_one;

Просмотреть файл

@ -1176,7 +1176,12 @@ dal_alloc_dma_pool(int lchip, int size)
if (use_high_memory)
{
dma_phy_base[lchip] = virt_to_bus(high_memory);
dma_virt_base[lchip] = ioremap_nocache(dma_phy_base[lchip], size);
/*
* ioremap has provided non-cached semantics by default
* since the Linux 2.6 days, so remove the additional
* ioremap_nocache interface.
*/
dma_virt_base[lchip] = ioremap(dma_phy_base[lchip], size);
}
else
{
@ -1205,7 +1210,12 @@ dal_alloc_dma_pool(int lchip, int size)
/* Get DMA memory from kernel */
dma_virt_base[lchip] = _dal_pgalloc(size);
dma_phy_base[lchip] = virt_to_bus(dma_virt_base[lchip]);
//dma_virt_base [lchip]= ioremap_nocache(dma_phy_base[lchip], size);
/*
* ioremap has provided non-cached semantics by default
* since the Linux 2.6 days, so remove the additional
* ioremap_nocache interface.
*/
//dma_virt_base [lchip]= ioremap(dma_phy_base[lchip], size);
#endif
}
}
@ -1943,7 +1953,12 @@ int linux_dal_pcie_probe(struct pci_dev* pdev, const struct pci_device_id* id)
}
dev->phys_address = pci_resource_start(pdev, bar);
dev->logic_address = (uintptr)ioremap_nocache(dev->phys_address,
/*
* ioremap has provided non-cached semantics by default
* since the Linux 2.6 days, so remove the additional
* ioremap_nocache interface.
*/
dev->logic_address = (uintptr)ioremap(dev->phys_address,
pci_resource_len(dev->pci_dev, bar));
/*0: little endian 1: big endian*/

Просмотреть файл

@ -1 +1,3 @@
obj-m := centec_e582_48x2q4z_platform.o centec_at24c64.o
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e582_48x2q4z_platform.o

Просмотреть файл

@ -1,602 +0,0 @@
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
* MicroChip 24LC, etc) won't much matter for typical read/write access.
* There are also I2C RAM chips, likewise interchangeable. One example
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
*
* However, misconfiguration can lose data. "Set 16-bit memory address"
* to a part with 8-bit addressing will overwrite data. Writing with too
* big a page size also loses data. And it's not safe to assume that the
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
* uses 0x51, for just one example.
*
* Accordingly, explicit board-specific configuration data should be used
* in almost all cases. (One partial exception is an SMBus used to access
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
*
* So this driver uses "new style" I2C driver binding, expecting to be
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
* similar kernel-resident tables; or, configuration data coming from
* a bootloader.
*
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
* which won't work on pure SMBus systems.
*/
struct at24_data {
struct at24_platform_data chip;
int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
*/
struct mutex lock;
struct bin_attribute bin;
u8 *writebuf;
unsigned write_max;
unsigned num_addresses;
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
*/
struct i2c_client *client[];
};
/*
* This parameter is to help this driver avoid blocking other drivers out
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
* clock, one 256 byte read takes about 1/43 second which is excessive;
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
*
* This value is forced to be a power of two so that writes align on pages.
*/
static unsigned io_limit = 128;
module_param(io_limit, uint, 0);
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
/*
* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8
#define AT24_BITMASK(x) (BIT(x) - 1)
/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) \
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
static const struct i2c_device_id at24_ctc_ids[] = {
{ "24c64-ctc", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16 | AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ctc_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
unsigned *offset)
{
unsigned i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
i = *offset >> 8;
*offset &= 0xff;
}
return at24->client[i];
}
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
struct i2c_client *client;
unsigned long timeout, read_time;
int status;
memset(msg, 0, sizeof(msg));
/*
* REVISIT some multi-address chips don't rollover page reads to
* the next slave address, so we may need to truncate the count.
* Those chips might need another quirk flag.
*
* If the real hardware used four adjacent 24c02 chips and that
* were misconfigured as one 24c08, that would be a similar effect:
* one "eeprom" file not four, but larger reads would fail when
* they crossed certain pages.
*/
/*
* Slave address and byte offset derive from the offset. Always
* set the byte address; on a multi-master board, another master
* may have changed the chip's "current" address pointer.
*/
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
count = 1;
/*
* Reads fail if the previous write didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
status = i2c_smbus_write_byte_data(client, (offset >> 8) & 0x0ff, offset & 0x0ff );
status = i2c_smbus_read_byte(client);
if (status >= 0) {
buf[0] = status;
status = count;
}
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
memset(buf, 0, count);
/*
* Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_read(at24, buf, off, count);
}
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
unsigned offset, size_t count)
{
struct i2c_client *client;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
if (offset + count > next_page)
count = next_page - offset;
/*
* Writes fail if the previous one didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
status = i2c_smbus_write_word_data(client, (offset >> 8) & 0x0ff, (offset & 0xFF) | buf[0]);
if (status == 0)
status = count;
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Write data to chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_write(at24, buf, off, 1); /* only one-byte to write; TODO page wirte */
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
if (unlikely(off >= attr->size))
return -EFBIG;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
if (node) {
if (of_get_property(node, "read-only", NULL))
chip->flags |= AT24_FLAG_READONLY;
val = of_get_property(node, "pagesize", NULL);
if (val)
chip->page_size = be32_to_cpup(val);
}
}
#else
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data)
return -ENODEV;
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms, so we better
* play safe. Specifying custom eeprom-types via platform_data
* is recommended anyhow.
*/
chip.page_size = 1;
/* update chipdata if OF is present */
at24_get_ofdata(client, &chip);
chip.setup = NULL;
chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
return -EINVAL;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus = I2C_SMBUS_WORD_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
return -EPFNOSUPPORT;
}
use_smbus = I2C_SMBUS_BYTE_DATA;
}
if (chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
at24->chip = chip;
at24->num_addresses = num_addresses;
printk(KERN_ALERT "at24_probe chip.byte_len = 0x%x\n", chip.byte_len);
printk(KERN_ALERT "at24_probe chip.flags = 0x%x\n", chip.flags);
printk(KERN_ALERT "at24_probe chip.magic = 0x%lx\n", id->driver_data);
printk(KERN_ALERT "at24_probe use_smbus = %d\n", at24->use_smbus);
printk(KERN_ALERT "at24_probe num_addresses = %d\n", at24->num_addresses);
/*
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsigned write_max = chip.page_size;
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
if (write_max > io_limit)
write_max = io_limit;
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max = I2C_SMBUS_BLOCK_MAX;
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = devm_kzalloc(&client->dev, write_max + 2, GFP_KERNEL);
if (!at24->writebuf)
return -ENOMEM;
} else {
dev_warn(&client->dev, "cannot write due to controller restrictions.");
}
}
at24->client[0] = client;
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
at24->client[i] = i2c_new_dummy(client->adapter, client->addr + i);
if (!at24->client[i]) {
dev_err(&client->dev, "address 0x%02x unavailable\n", client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
}
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
i2c_set_clientdata(client, at24);
printk(KERN_ALERT "at24_probe %s done\n", client->name);
return 0;
err_clients:
for (i = 1; i < num_addresses; i++)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
return err;
}
static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
at24 = i2c_get_clientdata(client);
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_board_info i2c_devs = {
I2C_BOARD_INFO("24c64-ctc", 0x57),
};
static struct i2c_adapter *adapter = NULL;
static struct i2c_client *client = NULL;
static int ctc_at24c64_init(void)
{
printk(KERN_ALERT "ctc_at24c64_init\n");
adapter = i2c_get_adapter(0);
if(adapter == NULL){
printk(KERN_ALERT "i2c_get_adapter == NULL\n");
return -1;
}
client = i2c_new_device(adapter, &i2c_devs);
if(client == NULL){
printk(KERN_ALERT "i2c_new_device == NULL\n");
i2c_put_adapter(adapter);
adapter = NULL;
return -1;
}
return 0;
}
static void ctc_at24c64_exit(void)
{
printk(KERN_ALERT "ctc_at24c64_exit\n");
if(client){
i2c_unregister_device(client);
}
if(adapter){
i2c_put_adapter(adapter);
}
}
static struct i2c_driver at24_ctc_driver = {
.driver = {
.name = "at24-ctc",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ctc_ids,
};
static int __init at24_ctc_init(void)
{
if (!io_limit) {
pr_err("at24_ctc: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
ctc_at24c64_init();
return i2c_add_driver(&at24_ctc_driver);
}
module_init(at24_ctc_init);
static void __exit at24_ctc_exit(void)
{
ctc_at24c64_exit();
i2c_del_driver(&at24_ctc_driver);
}
module_exit(at24_ctc_exit);
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
MODULE_AUTHOR("David Brownell and Wolfram Sang");
MODULE_LICENSE("GPL");
/* XXX */

Просмотреть файл

@ -1,7 +1,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include "../../pca954x/ctc-pca954x.h"
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/leds.h>
@ -95,7 +95,7 @@ static struct pca954x_platform_data i2c_dev_pca9548_platform_data = {
.num_modes = PCA9548_CHANNEL_NUM,
};
static struct i2c_board_info i2c_dev_pca9548 = {
I2C_BOARD_INFO("pca9548", 0x70),
I2C_BOARD_INFO("ctc_pca9548", 0x70),
.platform_data = &i2c_dev_pca9548_platform_data,
};
static struct i2c_client *i2c_client_pca9548x = NULL;
@ -110,7 +110,7 @@ static int e582_48x2q4z_init_i2c_pca9548(void)
}
/* install i2c-mux */
i2c_client_pca9548x = i2c_new_device(i2c_adp_master, &i2c_dev_pca9548);
i2c_client_pca9548x = i2c_new_client_device(i2c_adp_master, &i2c_dev_pca9548);
if(IS_INVALID_PTR(i2c_client_pca9548x))
{
i2c_client_pca9548x = NULL;
@ -150,7 +150,7 @@ static int e582_48x2q4z_init_i2c_adt7470(void)
return -1;
}
i2c_client_adt7470 = i2c_new_device(i2c_adp_adt7470, &i2c_dev_adt7470);
i2c_client_adt7470 = i2c_new_client_device(i2c_adp_adt7470, &i2c_dev_adt7470);
if(IS_INVALID_PTR(i2c_client_adt7470)){
i2c_client_adt7470 = NULL;
printk(KERN_CRIT "install e582_48x2q4z board adt7470 failed\n");
@ -206,14 +206,14 @@ static int e582_48x2q4z_init_i2c_psu(void)
return -1;
}
i2c_client_psu1 = i2c_new_device(i2c_adp_psu1, &i2c_dev_psu1);
i2c_client_psu1 = i2c_new_client_device(i2c_adp_psu1, &i2c_dev_psu1);
if(IS_INVALID_PTR(i2c_client_psu1)){
i2c_client_psu1 = NULL;
printk(KERN_CRIT "create e582_48x2q4z board i2c client psu1 failed\n");
return -1;
}
i2c_client_psu2 = i2c_new_device(i2c_adp_psu2, &i2c_dev_psu2);
i2c_client_psu2 = i2c_new_client_device(i2c_adp_psu2, &i2c_dev_psu2);
if(IS_INVALID_PTR(i2c_client_psu2)){
i2c_client_psu2 = NULL;
printk(KERN_CRIT "create e582_48x2q4z board i2c client psu2 failed\n");
@ -265,7 +265,7 @@ static int e582_48x2q4z_init_i2c_epld(void)
return -1;
}
i2c_client_epld = i2c_new_device(i2c_adp_master, &i2c_dev_epld);
i2c_client_epld = i2c_new_client_device(i2c_adp_master, &i2c_dev_epld);
if(IS_INVALID_PTR(i2c_client_epld))
{
i2c_client_epld = NULL;
@ -362,7 +362,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0);
i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0))
{
i2c_client_gpio0 = NULL;
@ -370,7 +370,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1);
i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1))
{
i2c_client_gpio1 = NULL;
@ -378,7 +378,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio2 = i2c_new_device(i2c_adp_gpio2, &i2c_dev_gpio2);
i2c_client_gpio2 = i2c_new_client_device(i2c_adp_gpio2, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2))
{
i2c_client_gpio2 = NULL;
@ -386,7 +386,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio3 = i2c_new_device(i2c_adp_gpio3, &i2c_dev_gpio3);
i2c_client_gpio3 = i2c_new_client_device(i2c_adp_gpio3, &i2c_dev_gpio3);
if(IS_INVALID_PTR(i2c_client_gpio3))
{
i2c_client_gpio3 = NULL;
@ -394,7 +394,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio4 = i2c_new_device(i2c_adp_gpio4, &i2c_dev_gpio4);
i2c_client_gpio4 = i2c_new_client_device(i2c_adp_gpio4, &i2c_dev_gpio4);
if(IS_INVALID_PTR(i2c_client_gpio4))
{
i2c_client_gpio4 = NULL;

Просмотреть файл

@ -25,12 +25,13 @@ if [ "$1" == "init" ]; then
modprobe i2c-dev
modprobe i2c-mux
modprobe i2c-smbus
modprobe i2c-mux-pca954x force_deselect_on_exit=1
modprobe ctc-i2c-mux-pca954x force_deselect_on_exit=1
i2cset -y 0 0x58 0x8 0x3f
modprobe lm77
modprobe tun
modprobe dal
modprobe centec_at24c64
modprobe at24
echo 24c64 0x57 > /sys/bus/i2c/devices/i2c-0/new_device
modprobe centec_e582_48x2q4z_platform
i2cset -y 15 0x21 0x18 0x0
i2cset -y 15 0x21 0x19 0x0
@ -69,9 +70,9 @@ elif [ "$1" == "deinit" ]; then
kill -9 $(pidof platform_monitor) > /dev/null 2>&1
rm -rf /usr/bin/platform_monitor
modprobe -r centec_e582_48x2q4z_platform
modprobe -r centec_at24c64
modprobe -r at24
modprobe -r dal
modprobe -r i2c-mux-pca954x
modprobe -r ctc-i2c-mux-pca954x
modprobe -r i2c-dev
else
echo "e582-48x2q4z_platform : Invalid option !"

Просмотреть файл

@ -1 +1,3 @@
obj-m := centec_e582_48x6q_platform.o centec_at24c64.o
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec/sonic-platform-modules-e582/pca954x/Module.symvers
obj-m := centec_e582_48x6q_platform.o

Просмотреть файл

@ -1,602 +0,0 @@
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
* MicroChip 24LC, etc) won't much matter for typical read/write access.
* There are also I2C RAM chips, likewise interchangeable. One example
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
*
* However, misconfiguration can lose data. "Set 16-bit memory address"
* to a part with 8-bit addressing will overwrite data. Writing with too
* big a page size also loses data. And it's not safe to assume that the
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
* uses 0x51, for just one example.
*
* Accordingly, explicit board-specific configuration data should be used
* in almost all cases. (One partial exception is an SMBus used to access
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
*
* So this driver uses "new style" I2C driver binding, expecting to be
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
* similar kernel-resident tables; or, configuration data coming from
* a bootloader.
*
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
* which won't work on pure SMBus systems.
*/
struct at24_data {
struct at24_platform_data chip;
int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
*/
struct mutex lock;
struct bin_attribute bin;
u8 *writebuf;
unsigned write_max;
unsigned num_addresses;
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
*/
struct i2c_client *client[];
};
/*
* This parameter is to help this driver avoid blocking other drivers out
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
* clock, one 256 byte read takes about 1/43 second which is excessive;
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
*
* This value is forced to be a power of two so that writes align on pages.
*/
static unsigned io_limit = 128;
module_param(io_limit, uint, 0);
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
/*
* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8
#define AT24_BITMASK(x) (BIT(x) - 1)
/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) \
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
static const struct i2c_device_id at24_ctc_ids[] = {
{ "24c64-ctc", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16 | AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ctc_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
unsigned *offset)
{
unsigned i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
i = *offset >> 8;
*offset &= 0xff;
}
return at24->client[i];
}
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
struct i2c_client *client;
unsigned long timeout, read_time;
int status;
memset(msg, 0, sizeof(msg));
/*
* REVISIT some multi-address chips don't rollover page reads to
* the next slave address, so we may need to truncate the count.
* Those chips might need another quirk flag.
*
* If the real hardware used four adjacent 24c02 chips and that
* were misconfigured as one 24c08, that would be a similar effect:
* one "eeprom" file not four, but larger reads would fail when
* they crossed certain pages.
*/
/*
* Slave address and byte offset derive from the offset. Always
* set the byte address; on a multi-master board, another master
* may have changed the chip's "current" address pointer.
*/
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
count = 1;
/*
* Reads fail if the previous write didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
status = i2c_smbus_write_byte_data(client, (offset >> 8) & 0x0ff, offset & 0x0ff );
status = i2c_smbus_read_byte(client);
if (status >= 0) {
buf[0] = status;
status = count;
}
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
memset(buf, 0, count);
/*
* Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_read(at24, buf, off, count);
}
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
unsigned offset, size_t count)
{
struct i2c_client *client;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
if (offset + count > next_page)
count = next_page - offset;
/*
* Writes fail if the previous one didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
status = i2c_smbus_write_word_data(client, (offset >> 8) & 0x0ff, (offset & 0xFF) | buf[0]);
if (status == 0)
status = count;
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Write data to chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_write(at24, buf, off, 1); /* only one-byte to write; TODO page wirte */
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
if (unlikely(off >= attr->size))
return -EFBIG;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
if (node) {
if (of_get_property(node, "read-only", NULL))
chip->flags |= AT24_FLAG_READONLY;
val = of_get_property(node, "pagesize", NULL);
if (val)
chip->page_size = be32_to_cpup(val);
}
}
#else
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data)
return -ENODEV;
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms, so we better
* play safe. Specifying custom eeprom-types via platform_data
* is recommended anyhow.
*/
chip.page_size = 1;
/* update chipdata if OF is present */
at24_get_ofdata(client, &chip);
chip.setup = NULL;
chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
return -EINVAL;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus = I2C_SMBUS_WORD_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
return -EPFNOSUPPORT;
}
use_smbus = I2C_SMBUS_BYTE_DATA;
}
if (chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
at24->chip = chip;
at24->num_addresses = num_addresses;
printk(KERN_ALERT "at24_probe chip.byte_len = 0x%x\n", chip.byte_len);
printk(KERN_ALERT "at24_probe chip.flags = 0x%x\n", chip.flags);
printk(KERN_ALERT "at24_probe chip.magic = 0x%lx\n", id->driver_data);
printk(KERN_ALERT "at24_probe use_smbus = %d\n", at24->use_smbus);
printk(KERN_ALERT "at24_probe num_addresses = %d\n", at24->num_addresses);
/*
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsigned write_max = chip.page_size;
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
if (write_max > io_limit)
write_max = io_limit;
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max = I2C_SMBUS_BLOCK_MAX;
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = devm_kzalloc(&client->dev, write_max + 2, GFP_KERNEL);
if (!at24->writebuf)
return -ENOMEM;
} else {
dev_warn(&client->dev, "cannot write due to controller restrictions.");
}
}
at24->client[0] = client;
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
at24->client[i] = i2c_new_dummy(client->adapter, client->addr + i);
if (!at24->client[i]) {
dev_err(&client->dev, "address 0x%02x unavailable\n", client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
}
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
i2c_set_clientdata(client, at24);
printk(KERN_ALERT "at24_probe %s done\n", client->name);
return 0;
err_clients:
for (i = 1; i < num_addresses; i++)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
return err;
}
static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
at24 = i2c_get_clientdata(client);
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_board_info i2c_devs = {
I2C_BOARD_INFO("24c64-ctc", 0x57),
};
static struct i2c_adapter *adapter = NULL;
static struct i2c_client *client = NULL;
static int ctc_at24c64_init(void)
{
printk(KERN_ALERT "ctc_at24c64_init\n");
adapter = i2c_get_adapter(0);
if(adapter == NULL){
printk(KERN_ALERT "i2c_get_adapter == NULL\n");
return -1;
}
client = i2c_new_device(adapter, &i2c_devs);
if(client == NULL){
printk(KERN_ALERT "i2c_new_device == NULL\n");
i2c_put_adapter(adapter);
adapter = NULL;
return -1;
}
return 0;
}
static void ctc_at24c64_exit(void)
{
printk(KERN_ALERT "ctc_at24c64_exit\n");
if(client){
i2c_unregister_device(client);
}
if(adapter){
i2c_put_adapter(adapter);
}
}
static struct i2c_driver at24_ctc_driver = {
.driver = {
.name = "at24-ctc",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ctc_ids,
};
static int __init at24_ctc_init(void)
{
if (!io_limit) {
pr_err("at24_ctc: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
ctc_at24c64_init();
return i2c_add_driver(&at24_ctc_driver);
}
module_init(at24_ctc_init);
static void __exit at24_ctc_exit(void)
{
ctc_at24c64_exit();
i2c_del_driver(&at24_ctc_driver);
}
module_exit(at24_ctc_exit);
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
MODULE_AUTHOR("David Brownell and Wolfram Sang");
MODULE_LICENSE("GPL");
/* XXX */

Просмотреть файл

@ -1,7 +1,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include "../../pca954x/ctc-pca954x.h"
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/leds.h>
@ -95,7 +95,7 @@ static struct pca954x_platform_data i2c_dev_pca9548_platform_data = {
.num_modes = PCA9548_CHANNEL_NUM,
};
static struct i2c_board_info i2c_dev_pca9548 = {
I2C_BOARD_INFO("pca9548", 0x70),
I2C_BOARD_INFO("ctc_pca9548", 0x70),
.platform_data = &i2c_dev_pca9548_platform_data,
};
static struct i2c_client *i2c_client_pca9548x = NULL;
@ -110,7 +110,7 @@ static int e582_48x6q_init_i2c_pca9548(void)
}
/* install i2c-mux */
i2c_client_pca9548x = i2c_new_device(i2c_adp_master, &i2c_dev_pca9548);
i2c_client_pca9548x = i2c_new_client_device(i2c_adp_master, &i2c_dev_pca9548);
if(IS_INVALID_PTR(i2c_client_pca9548x))
{
i2c_client_pca9548x = NULL;
@ -150,7 +150,7 @@ static int e582_48x6q_init_i2c_adt7470(void)
return -1;
}
i2c_client_adt7470 = i2c_new_device(i2c_adp_adt7470, &i2c_dev_adt7470);
i2c_client_adt7470 = i2c_new_client_device(i2c_adp_adt7470, &i2c_dev_adt7470);
if(IS_INVALID_PTR(i2c_client_adt7470)){
i2c_client_adt7470 = NULL;
printk(KERN_CRIT "install e582_48x6q board adt7470 failed\n");
@ -206,14 +206,14 @@ static int e582_48x6q_init_i2c_psu(void)
return -1;
}
i2c_client_psu1 = i2c_new_device(i2c_adp_psu1, &i2c_dev_psu1);
i2c_client_psu1 = i2c_new_client_device(i2c_adp_psu1, &i2c_dev_psu1);
if(IS_INVALID_PTR(i2c_client_psu1)){
i2c_client_psu1 = NULL;
printk(KERN_CRIT "create e582_48x6q board i2c client psu1 failed\n");
return -1;
}
i2c_client_psu2 = i2c_new_device(i2c_adp_psu2, &i2c_dev_psu2);
i2c_client_psu2 = i2c_new_client_device(i2c_adp_psu2, &i2c_dev_psu2);
if(IS_INVALID_PTR(i2c_client_psu2)){
i2c_client_psu2 = NULL;
printk(KERN_CRIT "create e582_48x6q board i2c client psu2 failed\n");
@ -265,7 +265,7 @@ static int e582_48x6q_init_i2c_epld(void)
return -1;
}
i2c_client_epld = i2c_new_device(i2c_adp_master, &i2c_dev_epld);
i2c_client_epld = i2c_new_client_device(i2c_adp_master, &i2c_dev_epld);
if(IS_INVALID_PTR(i2c_client_epld))
{
i2c_client_epld = NULL;
@ -317,7 +317,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio0 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio0);
i2c_client_gpio0 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0))
{
i2c_client_gpio0 = NULL;
@ -325,7 +325,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio1 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio1);
i2c_client_gpio1 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1))
{
i2c_client_gpio1 = NULL;
@ -333,7 +333,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio2 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio2);
i2c_client_gpio2 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2))
{
i2c_client_gpio2 = NULL;
@ -341,7 +341,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio3 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio3);
i2c_client_gpio3 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio3);
if(IS_INVALID_PTR(i2c_client_gpio3))
{
i2c_client_gpio3 = NULL;
@ -349,7 +349,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1;
}
i2c_client_gpio4 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio4);
i2c_client_gpio4 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio4);
if(IS_INVALID_PTR(i2c_client_gpio4))
{
i2c_client_gpio4 = NULL;

Просмотреть файл

@ -25,7 +25,7 @@ if [ "$1" == "init" ]; then
modprobe i2c-dev
modprobe i2c-mux
modprobe i2c-smbus
modprobe i2c-mux-pca954x force_deselect_on_exit=1
modprobe ctc-i2c-mux-pca954x force_deselect_on_exit=1
i2cset -y 0 0x58 0x8 0x3f
i2cset -y 0 0x20 0x1b 0x0
i2cset -y 0 0x20 0xb 0x0
@ -42,7 +42,8 @@ if [ "$1" == "init" ]; then
modprobe lm77
modprobe tun
modprobe dal
modprobe centec_at24c64
modprobe at24
echo 24c64 0x57 > /sys/bus/i2c/devices/i2c-0/new_device
modprobe centec_e582_48x6q_platform
#start platform monitor
@ -53,9 +54,9 @@ elif [ "$1" == "deinit" ]; then
kill -9 $(pidof platform_monitor) > /dev/null 2>&1
rm -rf /usr/bin/platform_monitor
modprobe -r centec_e582_48x6q_platform
modprobe -r centec_at24c64
modprobe -r at24
modprobe -r dal
modprobe -r i2c-mux-pca954x
modprobe -r ctc-i2c-mux-pca954x
modprobe -r i2c-dev
else
echo "e582-48x6q_platform : Invalid option !"

Просмотреть файл

@ -7,11 +7,11 @@ Standards-Version: 3.9.3
Package: platform-modules-e582-48x2q4z
Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned
Depends: linux-image-5.10.0-8-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e582-48x6q
Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned
Depends: linux-image-5.10.0-8-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp

Просмотреть файл

@ -6,6 +6,7 @@ KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd)
MODULE_DIRS:= 48x6q 48x2q4z
PCA954X_DIR := pca954x
%:
dh $@
@ -14,6 +15,9 @@ override_dh_auto_build:
(for mod in $(MODULE_DIRS); do \
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
done)
(for mod in $(PCA954X_DIR); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
done)
rm $(MOD_SRC_DIR)/centec-dal -rf
cp $(MOD_SRC_DIR)/../centec-dal $(MOD_SRC_DIR)/centec-dal -rf
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/centec-dal
@ -25,6 +29,7 @@ override_dh_auto_install:
cp -f $(MOD_SRC_DIR)/$${mod}/modules/*.ko \
debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp -f $(MOD_SRC_DIR)/centec-dal/*.ko debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp -f $(MOD_SRC_DIR)/$(PCA954X_DIR)/*.ko debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
done)
override_dh_usrlocal:

Просмотреть файл

@ -0,0 +1 @@
obj-m := ctc-i2c-mux-pca954x.o

Просмотреть файл

@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0
/*
* I2C multiplexer
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
*
* This module supports the PCA954x and PCA984x series of I2C multiplexer/switch
* chips made by NXP Semiconductors.
* This includes the:
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547,
* PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register. The upstream "parent" bus fans out to two,
* four, or eight downstream busses or channels; which of these
* are selected is determined by the chip type and register contents. A
* mux can select only one sub-bus at a time; a switch can select any
* combination simultaneously.
*
* Based on:
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
* Copyright (C) 2006
*
* Based on:
* pca954x.c from Ken Harrenstien
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <jdelvare@suse.de>.
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <dt-bindings/mux/mux.h>
#include "ctc-pca954x.h"
#define PCA954X_MAX_NCHANS 8
#define PCA954X_IRQ_OFFSET 4
enum pca_type {
pca_9540,
pca_9542,
pca_9543,
pca_9544,
pca_9545,
pca_9546,
pca_9547,
pca_9548,
pca_9846,
pca_9847,
pca_9848,
pca_9849,
};
struct chip_desc {
u8 nchans;
u8 enable; /* used for muxes only */
u8 has_irq;
enum muxtype {
pca954x_ismux = 0,
pca954x_isswi
} muxtype;
struct i2c_device_identity id;
};
struct pca954x {
const struct chip_desc *chip;
u8 last_chan; /* last register value */
/* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
s32 idle_state;
struct i2c_client *client;
struct irq_domain *irq;
unsigned int irq_mask;
raw_spinlock_t lock;
};
/* Provide specs for the PCA954x types we know about */
static const struct chip_desc chips[] = {
[pca_9540] = {
.nchans = 2,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9542] = {
.nchans = 2,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9543] = {
.nchans = 2,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9544] = {
.nchans = 4,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9545] = {
.nchans = 4,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9546] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9547] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9548] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9846] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10b,
},
},
[pca_9847] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x108,
},
},
[pca_9848] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10a,
},
},
[pca_9849] = {
.nchans = 4,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x109,
},
},
};
static const struct i2c_device_id pca954x_id[] = {
{ "ctc_pca9540", pca_9540 },
{ "ctc_pca9542", pca_9542 },
{ "ctc_pca9543", pca_9543 },
{ "ctc_pca9544", pca_9544 },
{ "ctc_pca9545", pca_9545 },
{ "ctc_pca9546", pca_9546 },
{ "ctc_pca9547", pca_9547 },
{ "ctc_pca9548", pca_9548 },
{ "ctc_pca9846", pca_9846 },
{ "ctc_pca9847", pca_9847 },
{ "ctc_pca9848", pca_9848 },
{ "ctc_pca9849", pca_9849 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,ctc_pca9540", .data = &chips[pca_9540] },
{ .compatible = "nxp,ctc_pca9542", .data = &chips[pca_9542] },
{ .compatible = "nxp,ctc_pca9543", .data = &chips[pca_9543] },
{ .compatible = "nxp,ctc_pca9544", .data = &chips[pca_9544] },
{ .compatible = "nxp,ctc_pca9545", .data = &chips[pca_9545] },
{ .compatible = "nxp,ctc_pca9546", .data = &chips[pca_9546] },
{ .compatible = "nxp,ctc_pca9547", .data = &chips[pca_9547] },
{ .compatible = "nxp,ctc_pca9548", .data = &chips[pca_9548] },
{ .compatible = "nxp,ctc_pca9846", .data = &chips[pca_9846] },
{ .compatible = "nxp,ctc_pca9847", .data = &chips[pca_9847] },
{ .compatible = "nxp,ctc_pca9848", .data = &chips[pca_9848] },
{ .compatible = "nxp,ctc_pca9849", .data = &chips[pca_9849] },
{}
};
MODULE_DEVICE_TABLE(of, pca954x_of_match);
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
union i2c_smbus_data dummy;
return __i2c_smbus_xfer(adap, client->addr, client->flags,
I2C_SMBUS_WRITE, val,
I2C_SMBUS_BYTE, &dummy);
}
static u8 pca954x_regval(struct pca954x *data, u8 chan)
{
/* We make switches look like muxes, not sure how to be smarter. */
if (data->chip->muxtype == pca954x_ismux)
return chan | data->chip->enable;
else
return 1 << chan;
}
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
u8 regval;
int ret = 0;
regval = pca954x_regval(data, chan);
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
data->last_chan = ret < 0 ? 0 : regval;
}
return ret;
}
static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
s32 idle_state;
idle_state = READ_ONCE(data->idle_state);
if (idle_state >= 0)
/* Set the mux back to a predetermined channel */
return pca954x_select_chan(muxc, idle_state);
if (idle_state == MUX_IDLE_DISCONNECT) {
/* Deselect active channel */
data->last_chan = 0;
return pca954x_reg_write(muxc->parent, client,
data->last_chan);
}
/* otherwise leave as-is */
return 0;
}
static ssize_t idle_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
}
static ssize_t idle_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
(val < 0 || val >= data->chip->nchans))
return -EINVAL;
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
WRITE_ONCE(data->idle_state, val);
/*
* Set the mux into a state consistent with the new
* idle_state.
*/
if (data->last_chan || val != MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(idle_state);
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
{
struct pca954x *data = dev_id;
unsigned long pending;
int ret, i;
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return IRQ_NONE;
pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1);
for_each_set_bit(i, &pending, data->chip->nchans)
handle_nested_irq(irq_linear_revmap(data->irq, i));
return IRQ_RETVAL(pending);
}
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
{
if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW)
return -EINVAL;
return 0;
}
static struct irq_chip pca954x_irq_chip = {
.name = "i2c-mux-pca954x",
.irq_set_type = pca954x_irq_set_type,
};
static int pca954x_irq_setup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
int c, irq;
if (!data->chip->has_irq || client->irq <= 0)
return 0;
raw_spin_lock_init(&data->lock);
data->irq = irq_domain_add_linear(client->dev.of_node,
data->chip->nchans,
&irq_domain_simple_ops, data);
if (!data->irq)
return -ENODEV;
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_create_mapping(data->irq, c);
if (!irq) {
dev_err(&client->dev, "failed irq create map\n");
return -EINVAL;
}
irq_set_chip_data(irq, data);
irq_set_chip_and_handler(irq, &pca954x_irq_chip,
handle_simple_irq);
}
return 0;
}
static void pca954x_cleanup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
int c, irq;
if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c);
irq_dispose_mapping(irq);
}
irq_domain_remove(data->irq);
}
i2c_mux_del_adapters(muxc);
}
static int pca954x_init(struct i2c_client *client, struct pca954x *data)
{
int ret;
if (data->idle_state >= 0)
data->last_chan = pca954x_regval(data, data->idle_state);
else
data->last_chan = 0; /* Disconnect multiplexer */
ret = i2c_smbus_write_byte(client, data->last_chan);
if (ret < 0)
data->last_chan = 0;
return ret;
}
/*
* I2C init/probing/exit functions
*/
static int pca954x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adap = client->adapter;
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct gpio_desc *gpio;
int num, force, class;
struct i2c_mux_core *muxc;
struct pca954x *data;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
pca954x_select_chan, pca954x_deselect_mux);
if (!muxc)
return -ENOMEM;
data = i2c_mux_priv(muxc);
i2c_set_clientdata(client, muxc);
data->client = client;
/* Reset the mux if a reset GPIO is specified. */
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
udelay(1);
gpiod_set_value_cansleep(gpio, 0);
/* Give the chip some time to recover. */
udelay(1);
}
data->chip = device_get_match_data(dev);
if (!data->chip)
data->chip = &chips[id->driver_data];
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
struct i2c_device_identity id;
ret = i2c_get_device_id(client, &id);
if (ret && ret != -EOPNOTSUPP)
return ret;
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
id.part_id != data->chip->id.part_id)) {
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
return -ENODEV;
}
}
data->idle_state = MUX_IDLE_AS_IS;
if (device_property_read_u32(dev, "idle-state", &data->idle_state)) {
if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
data->idle_state = MUX_IDLE_DISCONNECT;
}
/*
* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to a channel
* or disconnected state.
*/
ret = pca954x_init(client, data);
if (ret < 0) {
dev_warn(dev, "probe failed\n");
return -ENODEV;
}
ret = pca954x_irq_setup(muxc);
if (ret)
goto fail_cleanup;
/* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) {
force = 0; /* dynamic adap number */
class = 0; /* no class by default */
if (pdata) {
if (num < pdata->num_modes) {
/* force static number */
force = pdata->modes[num].adap_id;
class = pdata->modes[num].class;
} else
/* discard unconfigured channels */
break;
}
ret = i2c_mux_add_adapter(muxc, force, num, class);
if (ret)
goto fail_cleanup;
}
if (data->irq) {
ret = devm_request_threaded_irq(dev, data->client->irq,
NULL, pca954x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
"pca954x", data);
if (ret)
goto fail_cleanup;
}
/*
* The attr probably isn't going to be needed in most cases,
* so don't fail completely on error.
*/
device_create_file(dev, &dev_attr_idle_state);
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name);
return 0;
fail_cleanup:
pca954x_cleanup(muxc);
return ret;
}
static int pca954x_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
device_remove_file(&client->dev, &dev_attr_idle_state);
pca954x_cleanup(muxc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int pca954x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int ret;
ret = pca954x_init(client, data);
if (ret < 0)
dev_err(&client->dev, "failed to verify mux presence\n");
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "ctc_pca954x",
.pm = &pca954x_pm,
.of_match_table = pca954x_of_match,
},
.probe = pca954x_probe,
.remove = pca954x_remove,
.id_table = pca954x_id,
};
module_i2c_driver(pca954x_driver);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,48 @@
/*
*
* pca954x.h - I2C multiplexer/switch support
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@xxxxxxxx>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@xxxxxxxxxxx>
* Michael Lawnick <michael.lawnick.ext@xxxxxxx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _LINUX_I2C_PCA954X_H
#define _LINUX_I2C_PCA954X_H
/* Platform data for the PCA954x I2C multiplexers */
/* Per channel initialisation data:
* @adap_id: bus number for the adapter. 0 = don't care
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
* of this channel after transaction.
*
*/
struct pca954x_platform_mode {
int adap_id;
unsigned int deselect_on_exit:1;
unsigned int class;
};
/* Per mux/switch data, used with i2c_register_board_info */
struct pca954x_platform_data {
struct pca954x_platform_mode *modes;
int num_modes;
};
#endif /* _LINUX_I2C_PCA954X_H */

Просмотреть файл

@ -7,6 +7,6 @@ Standards-Version: 3.9.3
Package: platform-modules-embedway-es6220
Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned
Depends: linux-image-5.10.0-8-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp

Просмотреть файл

@ -20,12 +20,16 @@ generate_version_file()
if [ "$ENABLE_VERSION_CONTROL_DEB" != "y" ]; then
if [[ $CONFIGURED_ARCH == armhf || $CONFIGURED_ARCH == arm64 ]]; then
if [ $MULTIARCH_QEMU_ENVIRON == y ]; then
if [ $MULTIARCH_QEMU_ENVIRON == "y" ]; then
# qemu arm bin executable for cross-building
sudo mkdir -p $FILESYSTEM_ROOT/usr/bin
sudo cp /usr/bin/qemu*static $FILESYSTEM_ROOT/usr/bin || true
fi
sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://deb.debian.org/debian
sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --foreign --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://deb.debian.org/debian
sudo rm $FILESYSTEM_ROOT/proc -rf
sudo mkdir $FILESYSTEM_ROOT/proc
sudo mount -t proc proc $FILESYSTEM_ROOT/proc
sudo LANG=C chroot $FILESYSTEM_ROOT /debootstrap/debootstrap --second-stage
else
sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://debian-archive.trafficmanager.net/debian
fi

Просмотреть файл

@ -3,7 +3,9 @@ FROM multiarch/qemu-user-static:x86_64-arm-5.0.0-2 as qemu
FROM multiarch/debian-debootstrap:armhf-bullseye
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
{%- elif CONFIGURED_ARCH == "arm64" and MULTIARCH_QEMU_ENVIRON == "y" %}
FROM multiarch/qemu-user-static:x86_64-aarch64-5.2.0-2 as qemu
FROM multiarch/debian-debootstrap:arm64-bullseye
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
{%- else -%}
FROM debian:bullseye
{%- endif %}
@ -351,6 +353,11 @@ RUN apt-get update && apt-get install -y \
RUN apt-get -y build-dep openssh
# Build fix for ARM64 and ARMHF /etc/debian_version
{%- if CONFIGURED_ARCH == "armhf" or CONFIGURED_ARCH == "arm64" %}
RUN apt upgrade -y base-files
{%- endif %}
# Build fix for ARMHF bullseye libsairedis
{%- if CONFIGURED_ARCH == "armhf" %}
# Install doxygen build dependency packages