Compare commits
No commits in common. "644618d9c4223148c83cc2a7e43bb50ac2a03899" and "90e53ad77bb0ff4fd05e306ff509483493914a27" have entirely different histories.
644618d9c4
...
90e53ad77b
|
@ -28,8 +28,6 @@ make defconfig
|
||||||
# Enable collectd network encryption
|
# Enable collectd network encryption
|
||||||
echo "CONFIG_PACKAGE_COLLECTD_ENCRYPTED_NETWORK=y" >> .config
|
echo "CONFIG_PACKAGE_COLLECTD_ENCRYPTED_NETWORK=y" >> .config
|
||||||
|
|
||||||
make package/kmod-i2c-mux-pinctrl/compile
|
|
||||||
make package/kmod-rtc-pcf85063/compile
|
|
||||||
make package/asterisk-chan-quectel/compile
|
make package/asterisk-chan-quectel/compile
|
||||||
make package/collectd/compile
|
make package/collectd/compile
|
||||||
# no signing key
|
# no signing key
|
||||||
|
@ -40,12 +38,8 @@ cd ${pwd}
|
||||||
|
|
||||||
tar -xJf /Downloads/openwrt-imagebuilder-${OPENWRT_VERSION}-*.tar.xz
|
tar -xJf /Downloads/openwrt-imagebuilder-${OPENWRT_VERSION}-*.tar.xz
|
||||||
|
|
||||||
PROFILE=rpi-4
|
|
||||||
BINPATH=bin/targets/${OPENWRT_TARGET}/${OPENWRT_DEVICE}
|
|
||||||
IMAGE_PATH=${BINPATH}/openwrt-${OPENWRT_VERSION}-${OPENWRT_TARGET}-${OPENWRT_DEVICE}-${PROFILE}
|
|
||||||
|
|
||||||
cd openwrt-imagebuilder-*/
|
cd openwrt-imagebuilder-*/
|
||||||
cp ${sdkdir}/${BINPATH}/packages/*.ipk packages/
|
cp ${sdkdir}/bin/targets/bcm27xx/bcm2711/packages/*.ipk packages/
|
||||||
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/packages/*.ipk packages/
|
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/packages/*.ipk packages/
|
||||||
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/telephony/*.ipk packages/
|
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/telephony/*.ipk packages/
|
||||||
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/quectel/*.ipk packages/
|
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/quectel/*.ipk packages/
|
||||||
|
@ -57,9 +51,15 @@ echo "CONFIG_TARGET_ROOTFS_PARTSIZE=308" >> .config
|
||||||
# Disable ext4 images
|
# Disable ext4 images
|
||||||
echo "CONFIG_TARGET_ROOTFS_EXT4FS=n" >> .config
|
echo "CONFIG_TARGET_ROOTFS_EXT4FS=n" >> .config
|
||||||
|
|
||||||
PACKAGES="kmod-i2c-bcm2835 kmod-i2c-mux kmod-i2c-mux-pinctrl \
|
# Setup files
|
||||||
kmod-nf-nathelper-extra \
|
mkdir -p files/etc/dropbear
|
||||||
kmod-rtc-ds1307 kmod-rtc-pcf8563 kmod-rtc-pcf85063 \
|
chmod 0750 files/etc/dropbear
|
||||||
|
cp ${pwd}/authorized_keys files/etc/dropbear
|
||||||
|
chmod 0400 files/etc/dropbear/authorized_keys
|
||||||
|
mkdir -p files/etc/uci-defaults
|
||||||
|
cp ${pwd}/defaults/* files/etc/uci-defaults
|
||||||
|
|
||||||
|
PACKAGES="kmod-nf-nathelper-extra kmod-rtc-ds1307 \
|
||||||
kmod-usb-audio kmod-usb-net-rtl8152 \
|
kmod-usb-audio kmod-usb-net-rtl8152 \
|
||||||
alsa-lib pciutils usbutils \
|
alsa-lib pciutils usbutils \
|
||||||
kmod-usb-net-qmi-wwan libqmi qmi-utils uqmi luci-proto-qmi \
|
kmod-usb-net-qmi-wwan libqmi qmi-utils uqmi luci-proto-qmi \
|
||||||
|
@ -87,7 +87,7 @@ PACKAGES="kmod-i2c-bcm2835 kmod-i2c-mux kmod-i2c-mux-pinctrl \
|
||||||
collectd-mod-thermal collectd-mod-uptime \
|
collectd-mod-thermal collectd-mod-uptime \
|
||||||
collectd-mod-users collectd-mod-vmem \
|
collectd-mod-users collectd-mod-vmem \
|
||||||
collectd-mod-wireless \
|
collectd-mod-wireless \
|
||||||
curl etherwake fail2ban hwclock i2c-tools \
|
curl etherwake fail2ban \
|
||||||
luci-app-mwan3 luci-app-upnp \
|
luci-app-mwan3 luci-app-upnp \
|
||||||
luci-proto-wireguard \
|
luci-proto-wireguard \
|
||||||
luci-ssl-nginx luci-app-acme \
|
luci-ssl-nginx luci-app-acme \
|
||||||
|
@ -97,61 +97,13 @@ PACKAGES="kmod-i2c-bcm2835 kmod-i2c-mux kmod-i2c-mux-pinctrl \
|
||||||
wireguard-tools wget-ssl \
|
wireguard-tools wget-ssl \
|
||||||
"
|
"
|
||||||
|
|
||||||
BOOTSOURCE=target/linux/${OPENWRT_TARGET}/image
|
make image PROFILE=rpi-4 \
|
||||||
BOOTCONFIG=${BOOTSOURCE}/config.txt
|
|
||||||
KERNELSOURCE=build_dir/target-aarch64_cortex-a72_musl/linux-${OPENWRT_TARGET}_${OPENWRT_DEVICE}/linux-5.10.146
|
|
||||||
OVERLAYSOURCE=${KERNELSOURCE}/arch/arm64/boot/dts/overlays
|
|
||||||
|
|
||||||
# Setup files
|
|
||||||
mkdir -p files/etc/dropbear
|
|
||||||
chmod 0750 files/etc/dropbear
|
|
||||||
cp ${pwd}/authorized_keys files/etc/dropbear
|
|
||||||
chmod 0400 files/etc/dropbear/authorized_keys
|
|
||||||
mkdir -p files/etc/uci-defaults
|
|
||||||
cp ${pwd}/defaults/* files/etc/uci-defaults
|
|
||||||
|
|
||||||
cp ${pwd}/overlays/dualeth.txt ${BOOTSOURCE}/current.txt
|
|
||||||
|
|
||||||
cp ${BOOTCONFIG} .
|
|
||||||
|
|
||||||
cat << "EOF" >> ${BOOTCONFIG}
|
|
||||||
dtparam=i2c1=on
|
|
||||||
dtparam=spi=on
|
|
||||||
dtparam=i2s=on
|
|
||||||
dtoverlay=i2c-rtc,ds1307
|
|
||||||
|
|
||||||
include current.txt
|
|
||||||
EOF
|
|
||||||
|
|
||||||
pushd ${BOOTSOURCE}
|
|
||||||
git apply ${pwd}/patches/bootconfig-add.patch
|
|
||||||
popd
|
|
||||||
|
|
||||||
make image PROFILE=${PROFILE} \
|
|
||||||
PACKAGES="${PACKAGES}" DISABLED_SERVICES="dropbear" FILES="files"
|
PACKAGES="${PACKAGES}" DISABLED_SERVICES="dropbear" FILES="files"
|
||||||
|
|
||||||
mv ${IMAGE_PATH}-squashfs-factory.img.gz ${IMAGE_PATH}-squashfs-factory-dualeth.img.gz
|
make manifest PROFILE=rpi-4 PACKAGES="${PACKAGES}"
|
||||||
mv ${IMAGE_PATH}-squashfs-sysupgrade.img.gz ${IMAGE_PATH}-squashfs-sysupgrade-dualeth.img.gz
|
|
||||||
|
|
||||||
cp ${pwd}/overlays/sensing.txt ${BOOTSOURCE}/current.txt
|
|
||||||
cp ${pwd}/overlays/*.dtbo ${OVERLAYSOURCE}
|
|
||||||
cp config.txt ${BOOTCONFIG}
|
|
||||||
echo "include current.txt" >> ${BOOTCONFIG}
|
|
||||||
pushd ${KERNELSOURCE}
|
|
||||||
git apply ${pwd}/patches/overlay-add.patch
|
|
||||||
popd
|
|
||||||
|
|
||||||
make image PROFILE=${PROFILE} \
|
|
||||||
PACKAGES="${PACKAGES}" DISABLED_SERVICES="dropbear" FILES="files"
|
|
||||||
|
|
||||||
mv ${IMAGE_PATH}-squashfs-factory.img.gz ${IMAGE_PATH}-squashfs-factory-sensing.img.gz
|
|
||||||
mv ${IMAGE_PATH}-squashfs-sysupgrade.img.gz ${IMAGE_PATH}-squashfs-sysupgrade-sensing.img.gz
|
|
||||||
|
|
||||||
|
|
||||||
make manifest PROFILE=${PROFILE} PACKAGES="${PACKAGES}"
|
|
||||||
|
|
||||||
out=../../out
|
out=../../out
|
||||||
|
|
||||||
mkdir -p ${out}
|
mkdir -p ${out}
|
||||||
mv ${BINPATH}/*.gz ${out}
|
mv bin/targets/bcm27xx/bcm2711/*.gz ${out}
|
||||||
mv packages ${out}
|
mv packages ${out}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
include $(TOPDIR)/rules.mk
|
|
||||||
include $(INCLUDE_DIR)/kernel.mk
|
|
||||||
|
|
||||||
PKG_NAME:=i2c-mux-pinctrl
|
|
||||||
PKG_RELEASE:=1
|
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
|
||||||
|
|
||||||
define KernelPackage/$(PKG_NAME)
|
|
||||||
SUBMENU:=I2C support
|
|
||||||
TITLE:=pinctrl-based I2C multiplexer
|
|
||||||
DEPENDS:=+kmod-i2c-mux
|
|
||||||
AUTOLOAD:=$(call AutoLoad,51,i2c-mux-pinctrl,1)
|
|
||||||
FILES:=$(PKG_BUILD_DIR)/i2c-mux-pinctrl.ko
|
|
||||||
KCONFIG:=
|
|
||||||
endef
|
|
||||||
|
|
||||||
define KernelPackage/$(PKG_NAME)/description
|
|
||||||
Kernel modules for GENERIC_PINCTRL I2C bus mux/switching devices
|
|
||||||
endef
|
|
||||||
|
|
||||||
EXTRA_KCONFIG:= \
|
|
||||||
CONFIG_I2C_MUX_PINCTRL=m
|
|
||||||
|
|
||||||
EXTRA_CFLAGS:= \
|
|
||||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
|
|
||||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
|
|
||||||
|
|
||||||
MAKE_OPTS:= \
|
|
||||||
$(KERNEL_MAKE_FLAGS) \
|
|
||||||
M="$(PKG_BUILD_DIR)" \
|
|
||||||
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
|
||||||
$(EXTRA_KCONFIG)
|
|
||||||
|
|
||||||
define Build/Compile
|
|
||||||
$(MAKE) -C "$(LINUX_DIR)" \
|
|
||||||
$(MAKE_OPTS) \
|
|
||||||
modules
|
|
||||||
endef
|
|
||||||
|
|
||||||
$(eval $(call KernelPackage,$(PKG_NAME)))
|
|
|
@ -1,8 +0,0 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
#
|
|
||||||
# Makefile for multiplexer I2C chip drivers.
|
|
||||||
|
|
||||||
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
|
||||||
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
|
||||||
|
|
||||||
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
|
|
@ -1,184 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* Internal GPIO functions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013, Intel Corporation
|
|
||||||
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GPIOLIB_H
|
|
||||||
#define GPIOLIB_H
|
|
||||||
|
|
||||||
#include <linux/gpio/driver.h>
|
|
||||||
#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>
|
|
||||||
|
|
||||||
#define GPIOCHIP_NAME "gpiochip"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct gpio_device - internal state container for GPIO devices
|
|
||||||
* @id: numerical ID number for the GPIO chip
|
|
||||||
* @dev: the GPIO device struct
|
|
||||||
* @chrdev: character device for the GPIO device
|
|
||||||
* @mockdev: class device used by the deprecated sysfs interface (may be
|
|
||||||
* NULL)
|
|
||||||
* @owner: helps prevent removal of modules exporting active GPIOs
|
|
||||||
* @chip: pointer to the corresponding gpiochip, holding static
|
|
||||||
* data for this device
|
|
||||||
* @descs: array of ngpio descriptors.
|
|
||||||
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size
|
|
||||||
* of the @descs array.
|
|
||||||
* @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned
|
|
||||||
* at device creation time.
|
|
||||||
* @label: a descriptive name for the GPIO device, such as the part number
|
|
||||||
* or name of the IP component in a System on Chip.
|
|
||||||
* @data: per-instance data assigned by the driver
|
|
||||||
* @list: links gpio_device:s together for traversal
|
|
||||||
*
|
|
||||||
* This state container holds most of the runtime variable data
|
|
||||||
* for a GPIO device and can hold references and live on after the
|
|
||||||
* GPIO chip has been removed, if it is still being used from
|
|
||||||
* 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;
|
|
||||||
struct blocking_notifier_head notifier;
|
|
||||||
|
|
||||||
#ifdef CONFIG_PINCTRL
|
|
||||||
/*
|
|
||||||
* If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
|
|
||||||
* describe the actual pin range which they serve in an SoC. This
|
|
||||||
* information would be used by pinctrl subsystem to configure
|
|
||||||
* corresponding pins for gpio usage.
|
|
||||||
*/
|
|
||||||
struct list_head pin_ranges;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 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[];
|
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
|
||||||
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,
|
|
||||||
struct gpio_array *array_info,
|
|
||||||
unsigned long *value_bitmap);
|
|
||||||
|
|
||||||
extern spinlock_t gpio_lock;
|
|
||||||
extern struct list_head gpio_devices;
|
|
||||||
|
|
||||||
struct gpio_desc {
|
|
||||||
struct gpio_device *gdev;
|
|
||||||
unsigned long flags;
|
|
||||||
/* flag symbols are bit numbers */
|
|
||||||
#define FLAG_REQUESTED 0
|
|
||||||
#define FLAG_IS_OUT 1
|
|
||||||
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
|
|
||||||
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
|
|
||||||
#define FLAG_ACTIVE_LOW 6 /* value has active low */
|
|
||||||
#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;
|
|
||||||
/* Name of the GPIO */
|
|
||||||
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);
|
|
||||||
int gpiod_hog(struct gpio_desc *desc, const char *name,
|
|
||||||
unsigned long lflags, enum gpiod_flags dflags);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the GPIO number of the passed descriptor relative to its chip
|
|
||||||
*/
|
|
||||||
static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
|
|
||||||
{
|
|
||||||
return desc - &desc->gdev->descs[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* With descriptor prefix */
|
|
||||||
|
|
||||||
#define gpiod_emerg(desc, fmt, ...) \
|
|
||||||
pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
|
|
||||||
##__VA_ARGS__)
|
|
||||||
#define gpiod_crit(desc, fmt, ...) \
|
|
||||||
pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
|
||||||
##__VA_ARGS__)
|
|
||||||
#define gpiod_err(desc, fmt, ...) \
|
|
||||||
pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
|
||||||
##__VA_ARGS__)
|
|
||||||
#define gpiod_warn(desc, fmt, ...) \
|
|
||||||
pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
|
||||||
##__VA_ARGS__)
|
|
||||||
#define gpiod_info(desc, fmt, ...) \
|
|
||||||
pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
|
||||||
##__VA_ARGS__)
|
|
||||||
#define gpiod_dbg(desc, fmt, ...) \
|
|
||||||
pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
|
|
||||||
##__VA_ARGS__)
|
|
||||||
|
|
||||||
/* With chip prefix */
|
|
||||||
|
|
||||||
#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 */
|
|
|
@ -1,243 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
/*
|
|
||||||
* I2C multiplexer using GPIO API
|
|
||||||
*
|
|
||||||
* Peter Korsgaard <peter.korsgaard@barco.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/i2c-mux.h>
|
|
||||||
#include <linux/platform_data/i2c-mux-gpio.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/bits.h>
|
|
||||||
#include <linux/gpio/consumer.h>
|
|
||||||
/* FIXME: stop poking around inside gpiolib */
|
|
||||||
#include "gpiolib.h"
|
|
||||||
|
|
||||||
struct gpiomux {
|
|
||||||
struct i2c_mux_gpio_platform_data data;
|
|
||||||
int ngpios;
|
|
||||||
struct gpio_desc **gpios;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
|
||||||
{
|
|
||||||
DECLARE_BITMAP(values, BITS_PER_TYPE(val));
|
|
||||||
|
|
||||||
values[0] = val;
|
|
||||||
|
|
||||||
gpiod_set_array_value_cansleep(mux->ngpios, mux->gpios, NULL, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
|
|
||||||
{
|
|
||||||
struct gpiomux *mux = i2c_mux_priv(muxc);
|
|
||||||
|
|
||||||
i2c_mux_gpio_set(mux, chan);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
|
|
||||||
{
|
|
||||||
struct gpiomux *mux = i2c_mux_priv(muxc);
|
|
||||||
|
|
||||||
i2c_mux_gpio_set(mux, mux->data.idle);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
|
||||||
struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
struct device_node *adapter_np, *child;
|
|
||||||
struct i2c_adapter *adapter;
|
|
||||||
unsigned *values;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (!np)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
|
||||||
if (!adapter_np) {
|
|
||||||
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
|
||||||
of_node_put(adapter_np);
|
|
||||||
if (!adapter)
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
mux->data.parent = i2c_adapter_id(adapter);
|
|
||||||
put_device(&adapter->dev);
|
|
||||||
|
|
||||||
mux->data.n_values = of_get_child_count(np);
|
|
||||||
|
|
||||||
values = devm_kcalloc(&pdev->dev,
|
|
||||||
mux->data.n_values, sizeof(*mux->data.values),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!values) {
|
|
||||||
dev_err(&pdev->dev, "Cannot allocate values array");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
for_each_child_of_node(np, child) {
|
|
||||||
of_property_read_u32(child, "reg", values + i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
mux->data.values = values;
|
|
||||||
|
|
||||||
if (of_property_read_u32(np, "idle-state", &mux->data.idle))
|
|
||||||
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
|
||||||
struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct i2c_mux_core *muxc;
|
|
||||||
struct gpiomux *mux;
|
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct i2c_adapter *root;
|
|
||||||
unsigned initial_state;
|
|
||||||
int i, ngpios, ret;
|
|
||||||
|
|
||||||
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
|
||||||
if (!mux)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (!dev_get_platdata(&pdev->dev)) {
|
|
||||||
ret = i2c_mux_gpio_probe_dt(mux, pdev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
|
|
||||||
sizeof(mux->data));
|
|
||||||
}
|
|
||||||
|
|
||||||
ngpios = gpiod_count(&pdev->dev, "mux");
|
|
||||||
if (ngpios <= 0) {
|
|
||||||
dev_err(&pdev->dev, "no valid gpios provided\n");
|
|
||||||
return ngpios ?: -EINVAL;
|
|
||||||
}
|
|
||||||
mux->ngpios = ngpios;
|
|
||||||
|
|
||||||
parent = i2c_get_adapter(mux->data.parent);
|
|
||||||
if (!parent)
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
|
|
||||||
ngpios * sizeof(*mux->gpios), 0,
|
|
||||||
i2c_mux_gpio_select, NULL);
|
|
||||||
if (!muxc) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto alloc_failed;
|
|
||||||
}
|
|
||||||
mux->gpios = muxc->priv;
|
|
||||||
muxc->priv = mux;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, muxc);
|
|
||||||
|
|
||||||
root = i2c_root_adapter(&parent->dev);
|
|
||||||
|
|
||||||
muxc->mux_locked = true;
|
|
||||||
|
|
||||||
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
|
|
||||||
initial_state = mux->data.idle;
|
|
||||||
muxc->deselect = i2c_mux_gpio_deselect;
|
|
||||||
} else {
|
|
||||||
initial_state = mux->data.values[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ngpios; i++) {
|
|
||||||
struct device *gpio_dev;
|
|
||||||
struct gpio_desc *gpiod;
|
|
||||||
enum gpiod_flags flag;
|
|
||||||
|
|
||||||
if (initial_state & BIT(i))
|
|
||||||
flag = GPIOD_OUT_HIGH;
|
|
||||||
else
|
|
||||||
flag = GPIOD_OUT_LOW;
|
|
||||||
gpiod = devm_gpiod_get_index(&pdev->dev, "mux", i, flag);
|
|
||||||
if (IS_ERR(gpiod)) {
|
|
||||||
ret = PTR_ERR(gpiod);
|
|
||||||
goto alloc_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
mux->gpios[i] = gpiod;
|
|
||||||
|
|
||||||
if (!muxc->mux_locked)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* FIXME: find a proper way to access the GPIO device */
|
|
||||||
gpio_dev = &gpiod->gdev->dev;
|
|
||||||
muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (muxc->mux_locked)
|
|
||||||
dev_info(&pdev->dev, "mux-locked i2c mux\n");
|
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_values; i++) {
|
|
||||||
u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
|
||||||
unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
|
|
||||||
|
|
||||||
ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
|
|
||||||
if (ret)
|
|
||||||
goto add_adapter_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%d port mux on %s adapter\n",
|
|
||||||
mux->data.n_values, parent->name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
add_adapter_failed:
|
|
||||||
i2c_mux_del_adapters(muxc);
|
|
||||||
alloc_failed:
|
|
||||||
i2c_put_adapter(parent);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_mux_gpio_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
i2c_mux_del_adapters(muxc);
|
|
||||||
i2c_put_adapter(muxc->parent);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id i2c_mux_gpio_of_match[] = {
|
|
||||||
{ .compatible = "i2c-mux-gpio", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver i2c_mux_gpio_driver = {
|
|
||||||
.probe = i2c_mux_gpio_probe,
|
|
||||||
.remove = i2c_mux_gpio_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = "i2c-mux-gpio",
|
|
||||||
.of_match_table = i2c_mux_gpio_of_match,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module_platform_driver(i2c_mux_gpio_driver);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver");
|
|
||||||
MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_ALIAS("platform:i2c-mux-gpio");
|
|
|
@ -1,198 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
/*
|
|
||||||
* I2C multiplexer using pinctrl API
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/i2c-mux.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/pinctrl/consumer.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include "pinctrl_core.h"
|
|
||||||
|
|
||||||
struct i2c_mux_pinctrl {
|
|
||||||
struct pinctrl *pinctrl;
|
|
||||||
struct pinctrl_state *states[];
|
|
||||||
};
|
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
|
|
||||||
{
|
|
||||||
struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
|
|
||||||
|
|
||||||
return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan)
|
|
||||||
{
|
|
||||||
return i2c_mux_pinctrl_select(muxc, muxc->num_adapters);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct i2c_adapter *i2c_mux_pinctrl_root_adapter(
|
|
||||||
struct pinctrl_state *state)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *root = NULL;
|
|
||||||
struct pinctrl_setting *setting;
|
|
||||||
struct i2c_adapter *pin_root;
|
|
||||||
|
|
||||||
list_for_each_entry(setting, &state->settings, node) {
|
|
||||||
pin_root = i2c_root_adapter(setting->pctldev->dev);
|
|
||||||
if (!pin_root)
|
|
||||||
return NULL;
|
|
||||||
if (!root)
|
|
||||||
root = pin_root;
|
|
||||||
else if (root != pin_root)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev)
|
|
||||||
{
|
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
struct device_node *parent_np;
|
|
||||||
struct i2c_adapter *parent;
|
|
||||||
|
|
||||||
parent_np = of_parse_phandle(np, "i2c-parent", 0);
|
|
||||||
if (!parent_np) {
|
|
||||||
dev_err(dev, "Cannot parse i2c-parent\n");
|
|
||||||
return ERR_PTR(-ENODEV);
|
|
||||||
}
|
|
||||||
parent = of_find_i2c_adapter_by_node(parent_np);
|
|
||||||
of_node_put(parent_np);
|
|
||||||
if (!parent)
|
|
||||||
return ERR_PTR(-EPROBE_DEFER);
|
|
||||||
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
struct i2c_mux_core *muxc;
|
|
||||||
struct i2c_mux_pinctrl *mux;
|
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct i2c_adapter *root;
|
|
||||||
int num_names, i, ret;
|
|
||||||
const char *name;
|
|
||||||
|
|
||||||
num_names = of_property_count_strings(np, "pinctrl-names");
|
|
||||||
if (num_names < 0) {
|
|
||||||
dev_err(dev, "Cannot parse pinctrl-names: %d\n",
|
|
||||||
num_names);
|
|
||||||
return num_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = i2c_mux_pinctrl_parent_adapter(dev);
|
|
||||||
if (IS_ERR(parent))
|
|
||||||
return PTR_ERR(parent);
|
|
||||||
|
|
||||||
muxc = i2c_mux_alloc(parent, dev, num_names,
|
|
||||||
struct_size(mux, states, num_names),
|
|
||||||
0, i2c_mux_pinctrl_select, NULL);
|
|
||||||
if (!muxc) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_put_parent;
|
|
||||||
}
|
|
||||||
mux = i2c_mux_priv(muxc);
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, muxc);
|
|
||||||
|
|
||||||
mux->pinctrl = devm_pinctrl_get(dev);
|
|
||||||
if (IS_ERR(mux->pinctrl)) {
|
|
||||||
ret = PTR_ERR(mux->pinctrl);
|
|
||||||
dev_err(dev, "Cannot get pinctrl: %d\n", ret);
|
|
||||||
goto err_put_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < num_names; i++) {
|
|
||||||
ret = of_property_read_string_index(np, "pinctrl-names", i,
|
|
||||||
&name);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret);
|
|
||||||
goto err_put_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name);
|
|
||||||
if (IS_ERR(mux->states[i])) {
|
|
||||||
ret = PTR_ERR(mux->states[i]);
|
|
||||||
dev_err(dev, "Cannot look up pinctrl state %s: %d\n",
|
|
||||||
name, ret);
|
|
||||||
goto err_put_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(name, "idle"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i != num_names - 1) {
|
|
||||||
dev_err(dev, "idle state must be last\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto err_put_parent;
|
|
||||||
}
|
|
||||||
muxc->deselect = i2c_mux_pinctrl_deselect;
|
|
||||||
}
|
|
||||||
|
|
||||||
root = i2c_root_adapter(&muxc->parent->dev);
|
|
||||||
|
|
||||||
muxc->mux_locked = true;
|
|
||||||
for (i = 0; i < num_names; i++) {
|
|
||||||
if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) {
|
|
||||||
muxc->mux_locked = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (muxc->mux_locked)
|
|
||||||
dev_info(dev, "mux-locked i2c mux\n");
|
|
||||||
|
|
||||||
/* Do not add any adapter for the idle state (if it's there at all). */
|
|
||||||
for (i = 0; i < num_names - !!muxc->deselect; i++) {
|
|
||||||
ret = i2c_mux_add_adapter(muxc, 0, i, 0);
|
|
||||||
if (ret)
|
|
||||||
goto err_del_adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_del_adapter:
|
|
||||||
i2c_mux_del_adapters(muxc);
|
|
||||||
err_put_parent:
|
|
||||||
i2c_put_adapter(parent);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
i2c_mux_del_adapters(muxc);
|
|
||||||
i2c_put_adapter(muxc->parent);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id i2c_mux_pinctrl_of_match[] = {
|
|
||||||
{ .compatible = "i2c-mux-pinctrl", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver i2c_mux_pinctrl_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "i2c-mux-pinctrl",
|
|
||||||
.of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
|
|
||||||
},
|
|
||||||
.probe = i2c_mux_pinctrl_probe,
|
|
||||||
.remove = i2c_mux_pinctrl_remove,
|
|
||||||
};
|
|
||||||
module_platform_driver(i2c_mux_pinctrl_driver);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
|
|
||||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
MODULE_ALIAS("platform:i2c-mux-pinctrl");
|
|
|
@ -1,249 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
||||||
/*
|
|
||||||
* Core private header for the pin control subsystem
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 ST-Ericsson SA
|
|
||||||
* Written on behalf of Linaro for ST-Ericsson
|
|
||||||
*
|
|
||||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kref.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/radix-tree.h>
|
|
||||||
#include <linux/pinctrl/pinconf.h>
|
|
||||||
#include <linux/pinctrl/machine.h>
|
|
||||||
|
|
||||||
struct pinctrl_gpio_range;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl_dev - pin control class device
|
|
||||||
* @node: node to include this pin controller in the global pin controller list
|
|
||||||
* @desc: the pin controller descriptor supplied when initializing this pin
|
|
||||||
* controller
|
|
||||||
* @pin_desc_tree: each pin descriptor for this pin controller is stored in
|
|
||||||
* this radix tree
|
|
||||||
* @pin_group_tree: optionally each pin group can be stored in this radix tree
|
|
||||||
* @num_groups: optionally number of groups can be kept here
|
|
||||||
* @pin_function_tree: optionally each function can be stored in this radix tree
|
|
||||||
* @num_functions: optionally number of functions can be kept here
|
|
||||||
* @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
|
|
||||||
* ranges are added to this list at runtime
|
|
||||||
* @dev: the device entry for this pin controller
|
|
||||||
* @owner: module providing the pin controller, used for refcounting
|
|
||||||
* @driver_data: driver data for drivers registering to the pin controller
|
|
||||||
* subsystem
|
|
||||||
* @p: result of pinctrl_get() for this device
|
|
||||||
* @hog_default: default state for pins hogged by this device
|
|
||||||
* @hog_sleep: sleep state for pins hogged by this device
|
|
||||||
* @mutex: mutex taken on each pin controller specific action
|
|
||||||
* @device_root: debugfs root for this device
|
|
||||||
*/
|
|
||||||
struct pinctrl_dev {
|
|
||||||
struct list_head node;
|
|
||||||
struct pinctrl_desc *desc;
|
|
||||||
struct radix_tree_root pin_desc_tree;
|
|
||||||
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
|
|
||||||
struct radix_tree_root pin_group_tree;
|
|
||||||
unsigned int num_groups;
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
|
|
||||||
struct radix_tree_root pin_function_tree;
|
|
||||||
unsigned int num_functions;
|
|
||||||
#endif
|
|
||||||
struct list_head gpio_ranges;
|
|
||||||
struct device *dev;
|
|
||||||
struct module *owner;
|
|
||||||
void *driver_data;
|
|
||||||
struct pinctrl *p;
|
|
||||||
struct pinctrl_state *hog_default;
|
|
||||||
struct pinctrl_state *hog_sleep;
|
|
||||||
struct mutex mutex;
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
|
||||||
struct dentry *device_root;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl - per-device pin control state holder
|
|
||||||
* @node: global list node
|
|
||||||
* @dev: the device using this pin control handle
|
|
||||||
* @states: a list of states for this device
|
|
||||||
* @state: the current state
|
|
||||||
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
|
|
||||||
* this device, if any
|
|
||||||
* @users: reference count
|
|
||||||
*/
|
|
||||||
struct pinctrl {
|
|
||||||
struct list_head node;
|
|
||||||
struct device *dev;
|
|
||||||
struct list_head states;
|
|
||||||
struct pinctrl_state *state;
|
|
||||||
struct list_head dt_maps;
|
|
||||||
struct kref users;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl_state - a pinctrl state for a device
|
|
||||||
* @node: list node for struct pinctrl's @states field
|
|
||||||
* @name: the name of this state
|
|
||||||
* @settings: a list of settings for this state
|
|
||||||
*/
|
|
||||||
struct pinctrl_state {
|
|
||||||
struct list_head node;
|
|
||||||
const char *name;
|
|
||||||
struct list_head settings;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP
|
|
||||||
* @group: the group selector to program
|
|
||||||
* @func: the function selector to program
|
|
||||||
*/
|
|
||||||
struct pinctrl_setting_mux {
|
|
||||||
unsigned group;
|
|
||||||
unsigned func;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_*
|
|
||||||
* @group_or_pin: the group selector or pin ID to program
|
|
||||||
* @configs: a pointer to an array of config parameters/values to program into
|
|
||||||
* hardware. Each individual pin controller defines the format and meaning
|
|
||||||
* of config parameters.
|
|
||||||
* @num_configs: the number of entries in array @configs
|
|
||||||
*/
|
|
||||||
struct pinctrl_setting_configs {
|
|
||||||
unsigned group_or_pin;
|
|
||||||
unsigned long *configs;
|
|
||||||
unsigned num_configs;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl_setting - an individual mux or config setting
|
|
||||||
* @node: list node for struct pinctrl_settings's @settings field
|
|
||||||
* @type: the type of setting
|
|
||||||
* @pctldev: pin control device handling to be programmed. Not used for
|
|
||||||
* PIN_MAP_TYPE_DUMMY_STATE.
|
|
||||||
* @dev_name: the name of the device using this state
|
|
||||||
* @data: Data specific to the setting type
|
|
||||||
*/
|
|
||||||
struct pinctrl_setting {
|
|
||||||
struct list_head node;
|
|
||||||
enum pinctrl_map_type type;
|
|
||||||
struct pinctrl_dev *pctldev;
|
|
||||||
const char *dev_name;
|
|
||||||
union {
|
|
||||||
struct pinctrl_setting_mux mux;
|
|
||||||
struct pinctrl_setting_configs configs;
|
|
||||||
} data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pin_desc - pin descriptor for each physical pin in the arch
|
|
||||||
* @pctldev: corresponding pin control device
|
|
||||||
* @name: a name for the pin, e.g. the name of the pin/pad/finger on a
|
|
||||||
* datasheet or such
|
|
||||||
* @dynamic_name: if the name of this pin was dynamically allocated
|
|
||||||
* @drv_data: driver-defined per-pin data. pinctrl core does not touch this
|
|
||||||
* @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL.
|
|
||||||
* If non-zero, this pin is claimed by @owner. This field is an integer
|
|
||||||
* rather than a boolean, since pinctrl_get() might process multiple
|
|
||||||
* mapping table entries that refer to, and hence claim, the same group
|
|
||||||
* or pin, and each of these will increment the @usecount.
|
|
||||||
* @mux_owner: The name of device that called pinctrl_get().
|
|
||||||
* @mux_setting: The most recent selected mux setting for this pin, if any.
|
|
||||||
* @gpio_owner: If pinctrl_gpio_request() was called for this pin, this is
|
|
||||||
* the name of the GPIO that "owns" this pin.
|
|
||||||
*/
|
|
||||||
struct pin_desc {
|
|
||||||
struct pinctrl_dev *pctldev;
|
|
||||||
const char *name;
|
|
||||||
bool dynamic_name;
|
|
||||||
void *drv_data;
|
|
||||||
/* These fields only added when supporting pinmux drivers */
|
|
||||||
#ifdef CONFIG_PINMUX
|
|
||||||
unsigned mux_usecount;
|
|
||||||
const char *mux_owner;
|
|
||||||
const struct pinctrl_setting_mux *mux_setting;
|
|
||||||
const char *gpio_owner;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct pinctrl_maps - a list item containing part of the mapping table
|
|
||||||
* @node: mapping table list node
|
|
||||||
* @maps: array of mapping table entries
|
|
||||||
* @num_maps: the number of entries in @maps
|
|
||||||
*/
|
|
||||||
struct pinctrl_maps {
|
|
||||||
struct list_head node;
|
|
||||||
const struct pinctrl_map *maps;
|
|
||||||
unsigned num_maps;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct group_desc - generic pin group descriptor
|
|
||||||
* @name: name of the pin group
|
|
||||||
* @pins: array of pins that belong to the group
|
|
||||||
* @num_pins: number of pins in the group
|
|
||||||
* @data: pin controller driver specific data
|
|
||||||
*/
|
|
||||||
struct group_desc {
|
|
||||||
const char *name;
|
|
||||||
int *pins;
|
|
||||||
int num_pins;
|
|
||||||
void *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
int pinctrl_generic_get_group_count(struct pinctrl_dev *pctldev);
|
|
||||||
|
|
||||||
const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev,
|
|
||||||
unsigned int group_selector);
|
|
||||||
|
|
||||||
int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev,
|
|
||||||
unsigned int group_selector,
|
|
||||||
const unsigned int **pins,
|
|
||||||
unsigned int *npins);
|
|
||||||
|
|
||||||
struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev,
|
|
||||||
unsigned int group_selector);
|
|
||||||
|
|
||||||
int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name,
|
|
||||||
int *gpins, int ngpins, void *data);
|
|
||||||
|
|
||||||
int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev,
|
|
||||||
unsigned int group_selector);
|
|
||||||
|
|
||||||
#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);
|
|
||||||
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
|
|
||||||
const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
|
|
||||||
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
|
|
||||||
const char *pin_group);
|
|
||||||
|
|
||||||
static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
|
|
||||||
unsigned int pin)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
|
|
||||||
extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
|
|
||||||
|
|
||||||
extern struct mutex pinctrl_maps_mutex;
|
|
||||||
extern struct list_head pinctrl_maps;
|
|
||||||
|
|
||||||
#define for_each_maps(_maps_node_, _i_, _map_) \
|
|
||||||
list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
|
|
||||||
for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
|
|
||||||
_i_ < _maps_node_->num_maps; \
|
|
||||||
_i_++, _map_ = &_maps_node_->maps[_i_])
|
|
|
@ -1,42 +0,0 @@
|
||||||
include $(TOPDIR)/rules.mk
|
|
||||||
include $(INCLUDE_DIR)/kernel.mk
|
|
||||||
|
|
||||||
PKG_NAME:=rtc-pcf85063
|
|
||||||
PKG_RELEASE:=1
|
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
|
||||||
|
|
||||||
define KernelPackage/$(PKG_NAME)
|
|
||||||
SUBMENU:=Other modules
|
|
||||||
TITLE:=Philips PCF85063 RTC support
|
|
||||||
DEPENDS:=+kmod-i2c-core +kmod-regmap-i2c
|
|
||||||
AUTOLOAD:=$(call AutoProbe,rtc-pcf85063)
|
|
||||||
FILES:=$(PKG_BUILD_DIR)/rtc-pcf85063.ko
|
|
||||||
KCONFIG:=
|
|
||||||
endef
|
|
||||||
|
|
||||||
define KernelPackage/$(PKG_NAME)/description
|
|
||||||
Kernel module for Philips PCF85063 RTC chip.
|
|
||||||
endef
|
|
||||||
|
|
||||||
EXTRA_KCONFIG:= \
|
|
||||||
CONFIG_RTC_DRV_PCF85063=m \
|
|
||||||
CONFIG_RTC_CLASS=y
|
|
||||||
|
|
||||||
EXTRA_CFLAGS:= \
|
|
||||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
|
|
||||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
|
|
||||||
|
|
||||||
MAKE_OPTS:= \
|
|
||||||
$(KERNEL_MAKE_FLAGS) \
|
|
||||||
M="$(PKG_BUILD_DIR)" \
|
|
||||||
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
|
||||||
$(EXTRA_KCONFIG)
|
|
||||||
|
|
||||||
define Build/Compile
|
|
||||||
$(MAKE) -C "$(LINUX_DIR)" \
|
|
||||||
$(MAKE_OPTS) \
|
|
||||||
modules
|
|
||||||
endef
|
|
||||||
|
|
||||||
$(eval $(call KernelPackage,$(PKG_NAME)))
|
|
|
@ -1,8 +0,0 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
#
|
|
||||||
# Makefile for RTC class/drivers.
|
|
||||||
#
|
|
||||||
|
|
||||||
ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
|
|
||||||
|
|
||||||
obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
|
|
|
@ -1,642 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
/*
|
|
||||||
* An I2C driver for the PCF85063 RTC
|
|
||||||
* Copyright 2014 Rose Technology
|
|
||||||
*
|
|
||||||
* Author: Søren Andersen <san@rosetechnology.dk>
|
|
||||||
* Maintainers: http://www.nslu2-linux.org/
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 Micro Crystal AG
|
|
||||||
* Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
|
|
||||||
*/
|
|
||||||
#include <linux/clk-provider.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/bcd.h>
|
|
||||||
#include <linux/rtc.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/pm_wakeirq.h>
|
|
||||||
#include <linux/regmap.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Information for this driver was pulled from the following datasheets.
|
|
||||||
*
|
|
||||||
* https://www.nxp.com/documents/data_sheet/PCF85063A.pdf
|
|
||||||
* https://www.nxp.com/documents/data_sheet/PCF85063TP.pdf
|
|
||||||
*
|
|
||||||
* PCF85063A -- Rev. 6 — 18 November 2015
|
|
||||||
* PCF85063TP -- Rev. 4 — 6 May 2015
|
|
||||||
*
|
|
||||||
* https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8263-C7_App-Manual.pdf
|
|
||||||
* RV8263 -- Rev. 1.0 — January 2019
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PCF85063_REG_CTRL1 0x00 /* status */
|
|
||||||
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
|
|
||||||
#define PCF85063_REG_CTRL1_STOP BIT(5)
|
|
||||||
|
|
||||||
#define PCF85063_REG_CTRL2 0x01
|
|
||||||
#define PCF85063_CTRL2_AF BIT(6)
|
|
||||||
#define PCF85063_CTRL2_AIE BIT(7)
|
|
||||||
|
|
||||||
#define PCF85063_REG_OFFSET 0x02
|
|
||||||
#define PCF85063_OFFSET_SIGN_BIT 6 /* 2's complement sign bit */
|
|
||||||
#define PCF85063_OFFSET_MODE BIT(7)
|
|
||||||
#define PCF85063_OFFSET_STEP0 4340
|
|
||||||
#define PCF85063_OFFSET_STEP1 4069
|
|
||||||
|
|
||||||
#define PCF85063_REG_CLKO_F_MASK 0x07 /* frequency mask */
|
|
||||||
#define PCF85063_REG_CLKO_F_32768HZ 0x00
|
|
||||||
#define PCF85063_REG_CLKO_F_OFF 0x07
|
|
||||||
|
|
||||||
#define PCF85063_REG_RAM 0x03
|
|
||||||
|
|
||||||
#define PCF85063_REG_SC 0x04 /* datetime */
|
|
||||||
#define PCF85063_REG_SC_OS 0x80
|
|
||||||
|
|
||||||
#define PCF85063_REG_ALM_S 0x0b
|
|
||||||
#define PCF85063_AEN BIT(7)
|
|
||||||
|
|
||||||
struct pcf85063_config {
|
|
||||||
struct regmap_config regmap;
|
|
||||||
unsigned has_alarms:1;
|
|
||||||
unsigned force_cap_7000:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pcf85063 {
|
|
||||||
struct rtc_device *rtc;
|
|
||||||
struct regmap *regmap;
|
|
||||||
#ifdef CONFIG_COMMON_CLK
|
|
||||||
struct clk_hw clkout_hw;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
int rc;
|
|
||||||
u8 regs[7];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* while reading, the time/date registers are blocked and not updated
|
|
||||||
* anymore until the access is finished. To not lose a second
|
|
||||||
* event, the access must be finished within one second. So, read all
|
|
||||||
* time/date registers in one turn.
|
|
||||||
*/
|
|
||||||
rc = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_SC, regs,
|
|
||||||
sizeof(regs));
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* if the clock has lost its power it makes no sense to use its time */
|
|
||||||
if (regs[0] & PCF85063_REG_SC_OS) {
|
|
||||||
dev_warn(&pcf85063->rtc->dev, "Power loss detected, invalid time\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
tm->tm_sec = bcd2bin(regs[0] & 0x7F);
|
|
||||||
tm->tm_min = bcd2bin(regs[1] & 0x7F);
|
|
||||||
tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
|
|
||||||
tm->tm_mday = bcd2bin(regs[3] & 0x3F);
|
|
||||||
tm->tm_wday = regs[4] & 0x07;
|
|
||||||
tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */
|
|
||||||
tm->tm_year = bcd2bin(regs[6]);
|
|
||||||
tm->tm_year += 100;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
int rc;
|
|
||||||
u8 regs[7];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* to accurately set the time, reset the divider chain and keep it in
|
|
||||||
* reset state until all time/date registers are written
|
|
||||||
*/
|
|
||||||
rc = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
|
|
||||||
PCF85063_REG_CTRL1_STOP,
|
|
||||||
PCF85063_REG_CTRL1_STOP);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* hours, minutes and seconds */
|
|
||||||
regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */
|
|
||||||
|
|
||||||
regs[1] = bin2bcd(tm->tm_min);
|
|
||||||
regs[2] = bin2bcd(tm->tm_hour);
|
|
||||||
|
|
||||||
/* Day of month, 1 - 31 */
|
|
||||||
regs[3] = bin2bcd(tm->tm_mday);
|
|
||||||
|
|
||||||
/* Day, 0 - 6 */
|
|
||||||
regs[4] = tm->tm_wday & 0x07;
|
|
||||||
|
|
||||||
/* month, 1 - 12 */
|
|
||||||
regs[5] = bin2bcd(tm->tm_mon + 1);
|
|
||||||
|
|
||||||
/* year and century */
|
|
||||||
regs[6] = bin2bcd(tm->tm_year - 100);
|
|
||||||
|
|
||||||
/* write all registers at once */
|
|
||||||
rc = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_SC,
|
|
||||||
regs, sizeof(regs));
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write the control register as a separate action since the size of
|
|
||||||
* the register space is different between the PCF85063TP and
|
|
||||||
* PCF85063A devices. The rollover point can not be used.
|
|
||||||
*/
|
|
||||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
|
|
||||||
PCF85063_REG_CTRL1_STOP, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
u8 buf[4];
|
|
||||||
unsigned int val;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S,
|
|
||||||
buf, sizeof(buf));
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
alrm->time.tm_sec = bcd2bin(buf[0]);
|
|
||||||
alrm->time.tm_min = bcd2bin(buf[1]);
|
|
||||||
alrm->time.tm_hour = bcd2bin(buf[2]);
|
|
||||||
alrm->time.tm_mday = bcd2bin(buf[3]);
|
|
||||||
|
|
||||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
alrm->enabled = !!(val & PCF85063_CTRL2_AIE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
u8 buf[5];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
buf[0] = bin2bcd(alrm->time.tm_sec);
|
|
||||||
buf[1] = bin2bcd(alrm->time.tm_min);
|
|
||||||
buf[2] = bin2bcd(alrm->time.tm_hour);
|
|
||||||
buf[3] = bin2bcd(alrm->time.tm_mday);
|
|
||||||
buf[4] = PCF85063_AEN; /* Do not match on week day */
|
|
||||||
|
|
||||||
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
|
||||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S,
|
|
||||||
buf, sizeof(buf));
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
|
||||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
|
|
||||||
alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_rtc_alarm_irq_enable(struct device *dev,
|
|
||||||
unsigned int enabled)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
|
||||||
PCF85063_CTRL2_AIE,
|
|
||||||
enabled ? PCF85063_CTRL2_AIE : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_id;
|
|
||||||
unsigned int val;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
|
|
||||||
if (err)
|
|
||||||
return IRQ_NONE;
|
|
||||||
|
|
||||||
if (val & PCF85063_CTRL2_AF) {
|
|
||||||
rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF);
|
|
||||||
regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
|
||||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
|
|
||||||
0);
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IRQ_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_read_offset(struct device *dev, long *offset)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
long val;
|
|
||||||
u32 reg;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_OFFSET, ®);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
val = sign_extend32(reg & ~PCF85063_OFFSET_MODE,
|
|
||||||
PCF85063_OFFSET_SIGN_BIT);
|
|
||||||
|
|
||||||
if (reg & PCF85063_OFFSET_MODE)
|
|
||||||
*offset = val * PCF85063_OFFSET_STEP1;
|
|
||||||
else
|
|
||||||
*offset = val * PCF85063_OFFSET_STEP0;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_set_offset(struct device *dev, long offset)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
s8 mode0, mode1, reg;
|
|
||||||
unsigned int error0, error1;
|
|
||||||
|
|
||||||
if (offset > PCF85063_OFFSET_STEP0 * 63)
|
|
||||||
return -ERANGE;
|
|
||||||
if (offset < PCF85063_OFFSET_STEP0 * -64)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
mode0 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP0);
|
|
||||||
mode1 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP1);
|
|
||||||
|
|
||||||
error0 = abs(offset - (mode0 * PCF85063_OFFSET_STEP0));
|
|
||||||
error1 = abs(offset - (mode1 * PCF85063_OFFSET_STEP1));
|
|
||||||
if (mode1 > 63 || mode1 < -64 || error0 < error1)
|
|
||||||
reg = mode0 & ~PCF85063_OFFSET_MODE;
|
|
||||||
else
|
|
||||||
reg = mode1 | PCF85063_OFFSET_MODE;
|
|
||||||
|
|
||||||
return regmap_write(pcf85063->regmap, PCF85063_REG_OFFSET, reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_ioctl(struct device *dev, unsigned int cmd,
|
|
||||||
unsigned long arg)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
|
||||||
int status, ret = 0;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case RTC_VL_READ:
|
|
||||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &status);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
status = status & PCF85063_REG_SC_OS ? RTC_VL_DATA_INVALID : 0;
|
|
||||||
|
|
||||||
return put_user(status, (unsigned int __user *)arg);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct rtc_class_ops pcf85063_rtc_ops = {
|
|
||||||
.read_time = pcf85063_rtc_read_time,
|
|
||||||
.set_time = pcf85063_rtc_set_time,
|
|
||||||
.read_offset = pcf85063_read_offset,
|
|
||||||
.set_offset = pcf85063_set_offset,
|
|
||||||
.ioctl = pcf85063_ioctl,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct rtc_class_ops pcf85063_rtc_ops_alarm = {
|
|
||||||
.read_time = pcf85063_rtc_read_time,
|
|
||||||
.set_time = pcf85063_rtc_set_time,
|
|
||||||
.read_offset = pcf85063_read_offset,
|
|
||||||
.set_offset = pcf85063_set_offset,
|
|
||||||
.read_alarm = pcf85063_rtc_read_alarm,
|
|
||||||
.set_alarm = pcf85063_rtc_set_alarm,
|
|
||||||
.alarm_irq_enable = pcf85063_rtc_alarm_irq_enable,
|
|
||||||
.ioctl = pcf85063_ioctl,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pcf85063_nvmem_read(void *priv, unsigned int offset,
|
|
||||||
void *val, size_t bytes)
|
|
||||||
{
|
|
||||||
return regmap_read(priv, PCF85063_REG_RAM, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_nvmem_write(void *priv, unsigned int offset,
|
|
||||||
void *val, size_t bytes)
|
|
||||||
{
|
|
||||||
return regmap_write(priv, PCF85063_REG_RAM, *(u8 *)val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_load_capacitance(struct pcf85063 *pcf85063,
|
|
||||||
const struct device_node *np,
|
|
||||||
unsigned int force_cap)
|
|
||||||
{
|
|
||||||
u32 load = 7000;
|
|
||||||
u8 reg = 0;
|
|
||||||
|
|
||||||
if (force_cap)
|
|
||||||
load = force_cap;
|
|
||||||
else
|
|
||||||
of_property_read_u32(np, "quartz-load-femtofarads", &load);
|
|
||||||
|
|
||||||
switch (load) {
|
|
||||||
default:
|
|
||||||
dev_warn(&pcf85063->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
|
|
||||||
load);
|
|
||||||
fallthrough;
|
|
||||||
case 7000:
|
|
||||||
break;
|
|
||||||
case 12500:
|
|
||||||
reg = PCF85063_REG_CTRL1_CAP_SEL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
|
|
||||||
PCF85063_REG_CTRL1_CAP_SEL, reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMMON_CLK
|
|
||||||
/*
|
|
||||||
* Handling of the clkout
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define clkout_hw_to_pcf85063(_hw) container_of(_hw, struct pcf85063, clkout_hw)
|
|
||||||
|
|
||||||
static int clkout_rates[] = {
|
|
||||||
32768,
|
|
||||||
16384,
|
|
||||||
8192,
|
|
||||||
4096,
|
|
||||||
2048,
|
|
||||||
1024,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned long pcf85063_clkout_recalc_rate(struct clk_hw *hw,
|
|
||||||
unsigned long parent_rate)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
|
||||||
unsigned int buf;
|
|
||||||
int ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf);
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
buf &= PCF85063_REG_CLKO_F_MASK;
|
|
||||||
return clkout_rates[buf];
|
|
||||||
}
|
|
||||||
|
|
||||||
static long pcf85063_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
unsigned long *prate)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
|
||||||
if (clkout_rates[i] <= rate)
|
|
||||||
return clkout_rates[i];
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
unsigned long parent_rate)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
|
||||||
if (clkout_rates[i] == rate)
|
|
||||||
return regmap_update_bits(pcf85063->regmap,
|
|
||||||
PCF85063_REG_CTRL2,
|
|
||||||
PCF85063_REG_CLKO_F_MASK, i);
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_clkout_control(struct clk_hw *hw, bool enable)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
|
||||||
unsigned int buf;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_OFFSET, &buf);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
buf &= PCF85063_REG_CLKO_F_MASK;
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
if (buf == PCF85063_REG_CLKO_F_OFF)
|
|
||||||
buf = PCF85063_REG_CLKO_F_32768HZ;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
if (buf != PCF85063_REG_CLKO_F_OFF)
|
|
||||||
buf = PCF85063_REG_CLKO_F_OFF;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
|
||||||
PCF85063_REG_CLKO_F_MASK, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_clkout_prepare(struct clk_hw *hw)
|
|
||||||
{
|
|
||||||
return pcf85063_clkout_control(hw, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pcf85063_clkout_unprepare(struct clk_hw *hw)
|
|
||||||
{
|
|
||||||
pcf85063_clkout_control(hw, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf85063_clkout_is_prepared(struct clk_hw *hw)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
|
||||||
unsigned int buf;
|
|
||||||
int ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf);
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return (buf & PCF85063_REG_CLKO_F_MASK) != PCF85063_REG_CLKO_F_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct clk_ops pcf85063_clkout_ops = {
|
|
||||||
.prepare = pcf85063_clkout_prepare,
|
|
||||||
.unprepare = pcf85063_clkout_unprepare,
|
|
||||||
.is_prepared = pcf85063_clkout_is_prepared,
|
|
||||||
.recalc_rate = pcf85063_clkout_recalc_rate,
|
|
||||||
.round_rate = pcf85063_clkout_round_rate,
|
|
||||||
.set_rate = pcf85063_clkout_set_rate,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
|
|
||||||
{
|
|
||||||
struct clk *clk;
|
|
||||||
struct clk_init_data init;
|
|
||||||
struct device_node *node = pcf85063->rtc->dev.parent->of_node;
|
|
||||||
|
|
||||||
init.name = "pcf85063-clkout";
|
|
||||||
init.ops = &pcf85063_clkout_ops;
|
|
||||||
init.flags = 0;
|
|
||||||
init.parent_names = NULL;
|
|
||||||
init.num_parents = 0;
|
|
||||||
pcf85063->clkout_hw.init = &init;
|
|
||||||
|
|
||||||
/* optional override of the clockname */
|
|
||||||
of_property_read_string(node, "clock-output-names", &init.name);
|
|
||||||
|
|
||||||
/* register the clock */
|
|
||||||
clk = devm_clk_register(&pcf85063->rtc->dev, &pcf85063->clkout_hw);
|
|
||||||
|
|
||||||
if (!IS_ERR(clk))
|
|
||||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
|
||||||
|
|
||||||
return clk;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct pcf85063_config pcf85063a_config = {
|
|
||||||
.regmap = {
|
|
||||||
.reg_bits = 8,
|
|
||||||
.val_bits = 8,
|
|
||||||
.max_register = 0x11,
|
|
||||||
},
|
|
||||||
.has_alarms = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct pcf85063_config pcf85063tp_config = {
|
|
||||||
.regmap = {
|
|
||||||
.reg_bits = 8,
|
|
||||||
.val_bits = 8,
|
|
||||||
.max_register = 0x0a,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct pcf85063_config rv8263_config = {
|
|
||||||
.regmap = {
|
|
||||||
.reg_bits = 8,
|
|
||||||
.val_bits = 8,
|
|
||||||
.max_register = 0x11,
|
|
||||||
},
|
|
||||||
.has_alarms = 1,
|
|
||||||
.force_cap_7000 = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pcf85063_probe(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
struct pcf85063 *pcf85063;
|
|
||||||
unsigned int tmp;
|
|
||||||
int err;
|
|
||||||
const struct pcf85063_config *config = &pcf85063tp_config;
|
|
||||||
const void *data = of_device_get_match_data(&client->dev);
|
|
||||||
struct nvmem_config nvmem_cfg = {
|
|
||||||
.name = "pcf85063_nvram",
|
|
||||||
.reg_read = pcf85063_nvmem_read,
|
|
||||||
.reg_write = pcf85063_nvmem_write,
|
|
||||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
|
||||||
.size = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
dev_dbg(&client->dev, "%s\n", __func__);
|
|
||||||
|
|
||||||
pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!pcf85063)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
config = data;
|
|
||||||
|
|
||||||
pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap);
|
|
||||||
if (IS_ERR(pcf85063->regmap))
|
|
||||||
return PTR_ERR(pcf85063->regmap);
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, pcf85063);
|
|
||||||
|
|
||||||
err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL1, &tmp);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&client->dev, "RTC chip is not present\n");
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
pcf85063->rtc = devm_rtc_allocate_device(&client->dev);
|
|
||||||
if (IS_ERR(pcf85063->rtc))
|
|
||||||
return PTR_ERR(pcf85063->rtc);
|
|
||||||
|
|
||||||
err = pcf85063_load_capacitance(pcf85063, client->dev.of_node,
|
|
||||||
config->force_cap_7000 ? 7000 : 0);
|
|
||||||
if (err < 0)
|
|
||||||
dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
|
|
||||||
err);
|
|
||||||
|
|
||||||
pcf85063->rtc->ops = &pcf85063_rtc_ops;
|
|
||||||
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
|
||||||
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
|
||||||
pcf85063->rtc->uie_unsupported = 1;
|
|
||||||
|
|
||||||
if (config->has_alarms && client->irq > 0) {
|
|
||||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
|
||||||
NULL, pcf85063_rtc_handle_irq,
|
|
||||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
|
||||||
"pcf85063", pcf85063);
|
|
||||||
if (err) {
|
|
||||||
dev_warn(&pcf85063->rtc->dev,
|
|
||||||
"unable to request IRQ, alarms disabled\n");
|
|
||||||
} else {
|
|
||||||
pcf85063->rtc->ops = &pcf85063_rtc_ops_alarm;
|
|
||||||
device_init_wakeup(&client->dev, true);
|
|
||||||
err = dev_pm_set_wake_irq(&client->dev, client->irq);
|
|
||||||
if (err)
|
|
||||||
dev_err(&pcf85063->rtc->dev,
|
|
||||||
"failed to enable irq wake\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nvmem_cfg.priv = pcf85063->regmap;
|
|
||||||
rtc_nvmem_register(pcf85063->rtc, &nvmem_cfg);
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMMON_CLK
|
|
||||||
/* register clk in common clk framework */
|
|
||||||
pcf85063_clkout_register_clk(pcf85063);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return rtc_register_device(pcf85063->rtc);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static const struct of_device_id pcf85063_of_match[] = {
|
|
||||||
{ .compatible = "nxp,pcf85063", .data = &pcf85063tp_config },
|
|
||||||
{ .compatible = "nxp,pcf85063tp", .data = &pcf85063tp_config },
|
|
||||||
{ .compatible = "nxp,pcf85063a", .data = &pcf85063a_config },
|
|
||||||
{ .compatible = "microcrystal,rv8263", .data = &rv8263_config },
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, pcf85063_of_match);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct i2c_driver pcf85063_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "rtc-pcf85063",
|
|
||||||
.of_match_table = of_match_ptr(pcf85063_of_match),
|
|
||||||
},
|
|
||||||
.probe_new = pcf85063_probe,
|
|
||||||
};
|
|
||||||
|
|
||||||
module_i2c_driver(pcf85063_driver);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
|
|
||||||
MODULE_DESCRIPTION("PCF85063 RTC driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
|
@ -1,7 +0,0 @@
|
||||||
dtparam=i2c_vc=on
|
|
||||||
dtoverlay=i2c-rtc,pcf85063a,i2c_csi_dsi
|
|
||||||
|
|
||||||
[all]
|
|
||||||
|
|
||||||
[cm4]
|
|
||||||
otg_mode=1
|
|
|
@ -1,18 +0,0 @@
|
||||||
dtparam=i2c_arm=on
|
|
||||||
enable_uart=1
|
|
||||||
|
|
||||||
dtoverlay=ed-sdhost
|
|
||||||
dtoverlay=i2c-rtc,pcf8563
|
|
||||||
|
|
||||||
dtoverlay=spi1-1cs
|
|
||||||
dtparam=cs0_pin=18,cs0_spidev=disabled
|
|
||||||
dtoverlay=ed-mcp2515-spi1-can0
|
|
||||||
dtoverlay=uart2
|
|
||||||
dtoverlay=uart3
|
|
||||||
dtoverlay=uart4
|
|
||||||
dtoverlay=uart5
|
|
||||||
gpio=11=op,dl
|
|
||||||
[all]
|
|
||||||
|
|
||||||
[cm4]
|
|
||||||
otg_mode=1
|
|
|
@ -1,10 +0,0 @@
|
||||||
--- a/Makefile 2023-05-17 03:13:19.345089802 +0300
|
|
||||||
+++ b/Makefile 2023-05-17 03:15:55.564872084 +0300
|
|
||||||
@@ -22,6 +22,7 @@
|
|
||||||
mcopy -i $@.boot cmdline.txt ::
|
|
||||||
mcopy -i $@.boot config.txt ::
|
|
||||||
mcopy -i $@.boot distroconfig.txt ::
|
|
||||||
+ mcopy -i $@.boot current.txt ::
|
|
||||||
mcopy -i $@.boot $(IMAGE_KERNEL) ::$(KERNEL_IMG)
|
|
||||||
$(foreach dts,$(shell echo $(DEVICE_DTS)),mcopy -i $@.boot $(DTS_DIR)/$(dts).dtb ::;)
|
|
||||||
mmd -i $@.boot ::/overlays
|
|
|
@ -1,11 +0,0 @@
|
||||||
--- a/arch/arm64/boot/dts/overlays/Makefile 2023-02-24 04:54:15.978815530 +0200
|
|
||||||
+++ b/arch/arm64/boot/dts/overlays/Makefile 2023-02-24 04:57:05.155496795 +0200
|
|
||||||
@@ -3,6 +3,8 @@
|
|
||||||
dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb
|
|
||||||
|
|
||||||
dtbo-$(CONFIG_ARCH_BCM2835) += \
|
|
||||||
+ ed-mcp2515-spi1-can0.dtbo \
|
|
||||||
+ ed-sdhost.dtbo \
|
|
||||||
act-led.dtbo \
|
|
||||||
adafruit18.dtbo \
|
|
||||||
adau1977-adc.dtbo \
|
|
Loading…
Reference in New Issue
Block a user