You've already forked beagleconnect-freedom
mirror of
https://github.com/beagleboard/beagleconnect-freedom.git
synced 2026-08-05 05:20:23 +00:00
mikrobus driver works within buildroot but the click
device drivers are not being probed correctly:
in host /opt/gbridge.sh a hack for continously blinking the
led on the SensorTag/Launchpad is added(this background
transaction happening continously prevents some of the greybus
pm suspend events happening, which might have been the cause
for the kernel panic, with this continous background transfer
the kernel panic is not seen and qemu machine does not crash)
changes were tested using ATUSB, CC1352R SensorTag
Welcome to Buildroot
buildroot login: root
Password:
IFLP=`ifconfig | grep lowpan`
ip link set wpan0 down
iwpan phy phy0 set channel 0 26
iwpan dev wpan0 set pan_id 0xabcd
ip link add link wpan0 name lowpan0 type lowpan
ip link set lowpan0 up
ip link set wpan0 up
ip -6 addr add 2001:db8::2/64 dev lowpan0
sleep 1
killall -9 gbridge
sleep 1
sleep 5
gbridge > /dev/null 2&1 &
sleep 5
for i in `seq 1 100000`; do gpioset 0 6=1; gpioset 0 6=0; \
sleep 1; done
greybus 1-2.2: GMP VID=0x00000126, PID=0x00000126
greybus 1-2.2: DDBL1 Manufacturer=0x00000126, Product=0x00000126
greybus 1-2.2: excess descriptors in interface manifest
mikrobus:mikrobus_port_gb_register: mikrobus gb_probe , num cports= 3, manifest_size 252
mikrobus:mikrobus_port_gb_register: protocol added 11
mikrobus:mikrobus_port_gb_register: protocol added 3
mikrobus:mikrobus_port_gb_register: protocol added 2
mikrobus:mikrobus_port_register: registering port mikrobus-0
mikrobus_manifest:mikrobus_manifest_attach_device: parsed device 1, driver=bme280, protocol=3, reg=76
mikrobus_manifest:mikrobus_manifest_attach_device: parsed device 2, driver=opt3001, protocol=3, reg=44
mikrobus_manifest:mikrobus_manifest_attach_device: parsed device 3, driver=ams-iaq-core, protocol=3, reg=5a
mikrobus_manifest:mikrobus_manifest_parse: Greybus Service Sample Application manifest parsed with 3 devices
mikrobus mikrobus-0: registering device : bme280
mikrobus mikrobus-0: registering device : opt3001
mikrobus mikrobus-0: registering device : ams-iaq-core
0-0044 0-005a 0-0076 i2c-0
Mem: 29624K used, 471572K free, 84K shrd, 624K buff, 5152K cached
CPU: 7% usr 4% sys 0% nic 86% idle 0% io 0% irq 1% sirq
Load average: 0.22 0.10 0.03 1/56 378
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
193 178 root S 16060 3% 1% gbridge 2
178 174 root S 3744 1% 1% {gbridge.sh} /bin/sh -xv /opt/gbridge.
312 174 root R 1532 0% 1% top
47 2 root IW 0 0% 1% [kworker/0:3-eve]
10 2 root IW 0 0% 0% [rcu_sched]
9 2 root SW 0 0% 0% [ksoftirqd/0]
173 1 root S 3744 1% 0% /usr/sbin/iiod -D
165 1 avahi S 1924 0% 0% avahi-daemon: running [buildroot.local
174 1 root S 1548 0% 0% -sh
1 0 root S 1532 0% 0% init
175 1 root S 1528 0% 0% /sbin/getty -L tty1 0 vt100
150 1 root S 1528 0% 0% udhcpc -R -n -O search -p /var/run/udh
64 1 root S 1524 0% 0% /sbin/syslogd -n
68 1 root S 1520 0% 0% /sbin/klogd -n
378 178 root S 1520 0% 0% sleep 1
115 1 dbus S 1492 0% 0% dbus-daemon --system
169 1 root S 1080 0% 0% /usr/sbin/dropbear -R
29 2 root IW 0 0% 0% [kworker/u2:1-fl]
7 2 root IW 0 0% 0% [kworker/u2:0-gr]
13 2 root SW 0 0% 0% [kdevtmpfs]
Signed-off-by: Vaishnav M A <vaishnav@beagleboard.org>
2117 lines
63 KiB
Diff
2117 lines
63 KiB
Diff
From e0a3ef4d89c4d3540b8fe565a33ef0573df6d796 Mon Sep 17 00:00:00 2001
|
|
From: Vaishnav M A <vaishnav@beagleboard.org>
|
|
Date: Thu, 15 Oct 2020 17:05:50 +0530
|
|
Subject: [PATCH] mikrobus driver update patch
|
|
|
|
This Patch series is an update to the mikroBUS driver
|
|
with support for serdev devices and fixed regulator.
|
|
|
|
RFC v1 Patch : https://lkml.org/lkml/2020/7/24/518 .
|
|
The mikrobus driver is updated to add mikrobus ports from device-tree
|
|
overlays, the debug interfaces for adding mikrobus ports through sysFS
|
|
is removed, and the driver considers the extended usage of mikrobus
|
|
port pins from their standard purposes.
|
|
|
|
change log:
|
|
v2: support for adding mikroBUS ports from DT overlays,
|
|
remove debug sysFS interface for adding mikrobus ports,
|
|
consider extended pin usage/deviations from mikrobus standard
|
|
specifications,
|
|
use greybus CPort protocol enum instead of new protcol enums
|
|
Fix cases of wrong indendation, ignoring return values, freeing
|
|
allocated resources in case of errors and other style suggestions
|
|
in v1 review.
|
|
|
|
Signed-off-by: Vaishnav M A <vaishnav@beagleboard.org>
|
|
---
|
|
drivers/misc/Kconfig | 1 +
|
|
drivers/misc/Makefile | 1 +
|
|
drivers/misc/mikrobus/Kconfig | 16 +
|
|
drivers/misc/mikrobus/Makefile | 6 +
|
|
drivers/misc/mikrobus/mikrobus_core.c | 953 ++++++++++++++++++++++
|
|
drivers/misc/mikrobus/mikrobus_core.h | 201 +++++
|
|
drivers/misc/mikrobus/mikrobus_id.c | 207 +++++
|
|
drivers/misc/mikrobus/mikrobus_manifest.c | 475 +++++++++++
|
|
drivers/misc/mikrobus/mikrobus_manifest.h | 20 +
|
|
drivers/w1/w1_int.c | 28 +
|
|
include/linux/greybus/greybus_manifest.h | 49 ++
|
|
include/linux/w1.h | 1 +
|
|
12 files changed, 1958 insertions(+)
|
|
create mode 100644 drivers/misc/mikrobus/Kconfig
|
|
create mode 100644 drivers/misc/mikrobus/Makefile
|
|
create mode 100644 drivers/misc/mikrobus/mikrobus_core.c
|
|
create mode 100644 drivers/misc/mikrobus/mikrobus_core.h
|
|
create mode 100644 drivers/misc/mikrobus/mikrobus_id.c
|
|
create mode 100644 drivers/misc/mikrobus/mikrobus_manifest.c
|
|
create mode 100644 drivers/misc/mikrobus/mikrobus_manifest.h
|
|
|
|
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
|
index 9252c020fe50..98d6be2597c8 100644
|
|
--- a/drivers/misc/Kconfig
|
|
+++ b/drivers/misc/Kconfig
|
|
@@ -473,4 +473,5 @@ source "drivers/misc/ocxl/Kconfig"
|
|
source "drivers/misc/cardreader/Kconfig"
|
|
source "drivers/misc/habanalabs/Kconfig"
|
|
source "drivers/misc/uacce/Kconfig"
|
|
+source "drivers/misc/mikrobus/Kconfig"
|
|
endmenu
|
|
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
|
index 42a1b0b64232..f0ca0234c8b2 100644
|
|
--- a/drivers/misc/Makefile
|
|
+++ b/drivers/misc/Makefile
|
|
@@ -40,6 +40,7 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
|
|
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
|
|
obj-y += ti-st/
|
|
obj-y += lis3lv02d/
|
|
+obj-y += mikrobus/
|
|
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
|
|
obj-$(CONFIG_INTEL_MEI) += mei/
|
|
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
|
|
diff --git a/drivers/misc/mikrobus/Kconfig b/drivers/misc/mikrobus/Kconfig
|
|
new file mode 100644
|
|
index 000000000000..5f42bc4e9410
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/Kconfig
|
|
@@ -0,0 +1,16 @@
|
|
+menuconfig MIKROBUS
|
|
+ tristate "Module for instantiating devices on mikroBUS ports"
|
|
+ help
|
|
+ This option enables the mikroBUS driver. mikroBUS is an add-on
|
|
+ board socket standard that offers maximum expandability with
|
|
+ the smallest number of pins. The mikroBUS driver instantiates
|
|
+ devices on a mikroBUS port described by identifying data present
|
|
+ in an add-on board resident EEPROM, more details on the mikroBUS
|
|
+ driver support and discussion can be found in this eLinux wiki :
|
|
+ elinux.org/Mikrobus
|
|
+
|
|
+
|
|
+ Say Y here to enable support for this driver.
|
|
+
|
|
+ To compile this code as a module, chose M here: the module
|
|
+ will be called mikrobus.ko
|
|
diff --git a/drivers/misc/mikrobus/Makefile b/drivers/misc/mikrobus/Makefile
|
|
new file mode 100644
|
|
index 000000000000..0dfd04eabaf6
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/Makefile
|
|
@@ -0,0 +1,6 @@
|
|
+# SPDX-License-Identifier: GPL-2.0
|
|
+# mikroBUS Core
|
|
+
|
|
+obj-$(CONFIG_MIKROBUS) += mikrobus.o
|
|
+mikrobus-y := mikrobus_core.o mikrobus_manifest.o
|
|
+obj-$(CONFIG_MIKROBUS) += mikrobus_id.o
|
|
\ No newline at end of file
|
|
diff --git a/drivers/misc/mikrobus/mikrobus_core.c b/drivers/misc/mikrobus/mikrobus_core.c
|
|
new file mode 100644
|
|
index 000000000000..07f9f3cc9991
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/mikrobus_core.c
|
|
@@ -0,0 +1,953 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * mikroBUS driver for instantiating add-on
|
|
+ * board devices with an identifier EEPROM
|
|
+ *
|
|
+ * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "mikrobus:%s: " fmt, __func__
|
|
+
|
|
+#include <linux/err.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/idr.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/jump_label.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/w1.h>
|
|
+#include <linux/w1-gpio.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/gpio/machine.h>
|
|
+#include <linux/nvmem-consumer.h>
|
|
+#include <linux/nvmem-provider.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/spi/spi.h>
|
|
+#include <linux/serdev.h>
|
|
+#include <linux/property.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/pinctrl/pinctrl.h>
|
|
+#include <linux/pinctrl/pinmux.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
+#include <linux/regulator/fixed.h>
|
|
+#include <linux/regulator/machine.h>
|
|
+#include <linux/clk-provider.h>
|
|
+#include <linux/greybus/greybus_manifest.h>
|
|
+
|
|
+#include "mikrobus_core.h"
|
|
+#include "mikrobus_manifest.h"
|
|
+
|
|
+static DEFINE_MUTEX(core_lock);
|
|
+static DEFINE_IDR(mikrobus_port_idr);
|
|
+static struct class_compat *mikrobus_port_compat_class;
|
|
+int __mikrobus_first_dynamic_bus_num;
|
|
+static bool is_registered;
|
|
+static int mikrobus_port_id_eeprom_probe(struct mikrobus_port *port);
|
|
+
|
|
+const char *MIKROBUS_PINCTRL_STR[] = {"pwm", "uart", "i2c", "spi"};
|
|
+
|
|
+struct bus_type mikrobus_bus_type = {
|
|
+ .name = "mikrobus",
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(mikrobus_bus_type);
|
|
+
|
|
+int mikrobus_port_scan_eeprom(struct mikrobus_port *port)
|
|
+{
|
|
+ struct addon_board_info *board;
|
|
+ int manifest_size;
|
|
+ char header[12];
|
|
+ int retval;
|
|
+ char *buf;
|
|
+ retval = nvmem_device_read(port->eeprom, 0, 12, header);
|
|
+ if (retval != 12) {
|
|
+ dev_err(&port->dev, "failed to fetch manifest header %d\n",
|
|
+ retval);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ manifest_size = mikrobus_manifest_header_validate(header, 12);
|
|
+ if (manifest_size < 0) {
|
|
+ dev_err(&port->dev, "invalid manifest size %d\n",
|
|
+ manifest_size);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ buf = kzalloc(manifest_size, GFP_KERNEL);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+ retval = nvmem_device_read(port->eeprom, 0, manifest_size, buf);
|
|
+ if (retval != manifest_size) {
|
|
+ dev_err(&port->dev, "failed to fetch manifest %d\n", retval);
|
|
+ retval = -EINVAL;
|
|
+ goto err_free_buf;
|
|
+ }
|
|
+ board = kzalloc(sizeof(*board), GFP_KERNEL);
|
|
+ if (!board) {
|
|
+ retval = -ENOMEM;
|
|
+ goto err_free_buf;
|
|
+ }
|
|
+ w1_reset_bus(port->w1_master);
|
|
+ w1_write_8(port->w1_master, MIKROBUS_EEPROM_EXIT_ID_CMD);
|
|
+ set_bit(W1_ABORT_SEARCH, &port->w1_master->flags);
|
|
+ INIT_LIST_HEAD(&board->manifest_descs);
|
|
+ INIT_LIST_HEAD(&board->devices);
|
|
+ retval = mikrobus_manifest_parse(board, buf, manifest_size);
|
|
+ if (!retval) {
|
|
+ dev_err(&port->dev, "failed to parse manifest, size %d\n",
|
|
+ manifest_size);
|
|
+ retval = -EINVAL;
|
|
+ goto err_free_board;
|
|
+ }
|
|
+ retval = mikrobus_board_register(port, board);
|
|
+ if (retval) {
|
|
+ dev_err(&port->dev, "failed to register board %s\n",
|
|
+ board->name);
|
|
+ goto err_free_board;
|
|
+ }
|
|
+ kfree(buf);
|
|
+ return 0;
|
|
+err_free_board:
|
|
+ kfree(board);
|
|
+err_free_buf:
|
|
+ kfree(buf);
|
|
+ return retval;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mikrobus_port_scan_eeprom);
|
|
+
|
|
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sprintf(buf, "%s\n", to_mikrobus_port(dev)->name);
|
|
+}
|
|
+static DEVICE_ATTR_RO(name);
|
|
+
|
|
+static ssize_t new_device_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct mikrobus_port *port = to_mikrobus_port(dev);
|
|
+ struct addon_board_info *board;
|
|
+ int retval;
|
|
+
|
|
+ if (port->board) {
|
|
+ dev_err(dev, "already has board registered\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ board = kzalloc(sizeof(*board), GFP_KERNEL);
|
|
+ if (!board)
|
|
+ return -ENOMEM;
|
|
+ INIT_LIST_HEAD(&board->manifest_descs);
|
|
+ INIT_LIST_HEAD(&board->devices);
|
|
+ retval = mikrobus_manifest_parse(board, (void *)buf, count);
|
|
+ if (!retval) {
|
|
+ dev_err(dev, "failed to parse manifest\n");
|
|
+ retval = -EINVAL;
|
|
+ goto err_free_board;
|
|
+ }
|
|
+ retval = mikrobus_board_register(port, board);
|
|
+ if (retval) {
|
|
+ dev_err(dev, "failed to register board %s\n", board->name);
|
|
+ retval = -EINVAL;
|
|
+ goto err_free_board;
|
|
+ }
|
|
+ return count;
|
|
+err_free_board:
|
|
+ kfree(board);
|
|
+ return retval;
|
|
+}
|
|
+static DEVICE_ATTR_WO(new_device);
|
|
+
|
|
+static ssize_t rescan_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct mikrobus_port *port = to_mikrobus_port(dev);
|
|
+ unsigned long id;
|
|
+ int retval;
|
|
+ int i;
|
|
+
|
|
+ if (kstrtoul(buf, 0, &id)) {
|
|
+ dev_err(dev, "cannot parse trigger\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (port->board) {
|
|
+ dev_err(dev, "already has board registered\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ if (!port->eeprom){
|
|
+ return mikrobus_port_id_eeprom_probe(port);
|
|
+ }
|
|
+ /* Enter ID Mode */
|
|
+ sprintf(port->pinctrl_selected[MIKROBUS_PINCTRL_SPI], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[MIKROBUS_PINCTRL_SPI], MIKROBUS_PINCTRL_STATE_GPIO);
|
|
+
|
|
+ retval = mikrobus_port_pinctrl_select(port);
|
|
+ /* set MOSI LOW, SCK HIGH */
|
|
+ gpiod_direction_output(port->gpios->desc[MIKROBUS_PIN_MOSI], 0);
|
|
+ gpiod_direction_output(port->gpios->desc[MIKROBUS_PIN_SCK], 1);
|
|
+ msleep(100);
|
|
+ for( i = 0; i < 4; i++){
|
|
+ gpiod_set_value(port->gpios->desc[MIKROBUS_PIN_MOSI] , 1);
|
|
+ udelay(1000);
|
|
+ gpiod_set_value(port->gpios->desc[MIKROBUS_PIN_MOSI] , 0);
|
|
+ udelay(1000);
|
|
+ }
|
|
+ msleep(100); /* temporary delay to fix ROM ID copy */
|
|
+
|
|
+ retval = mikrobus_port_scan_eeprom(port);
|
|
+ if (retval) {
|
|
+ dev_err(dev, "board register from manifest failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return count;
|
|
+}
|
|
+static DEVICE_ATTR_WO(rescan);
|
|
+
|
|
+static ssize_t delete_device_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct mikrobus_port *port = to_mikrobus_port(dev);
|
|
+ unsigned long id;
|
|
+
|
|
+ if (kstrtoul(buf, 0, &id)) {
|
|
+ dev_err(dev, "cannot parse board id");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (!port->board) {
|
|
+ dev_err(dev, "does not have registered boards");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ mikrobus_board_unregister(port, port->board);
|
|
+ return count;
|
|
+}
|
|
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL, delete_device_store);
|
|
+
|
|
+static struct attribute *mikrobus_port_attrs[] = {
|
|
+ &dev_attr_new_device.attr, &dev_attr_rescan.attr,
|
|
+ &dev_attr_delete_device.attr, &dev_attr_name.attr, NULL};
|
|
+ATTRIBUTE_GROUPS(mikrobus_port);
|
|
+
|
|
+static void mikrobus_port_release(struct device *dev)
|
|
+{
|
|
+ struct mikrobus_port *port = to_mikrobus_port(dev);
|
|
+
|
|
+ mutex_lock(&core_lock);
|
|
+ idr_remove(&mikrobus_port_idr, port->id);
|
|
+ mutex_unlock(&core_lock);
|
|
+ kfree(port);
|
|
+}
|
|
+
|
|
+struct device_type mikrobus_port_type = {
|
|
+ .groups = mikrobus_port_groups,
|
|
+ .release = mikrobus_port_release,
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(mikrobus_port_type);
|
|
+
|
|
+static int mikrobus_w1_master_match(struct device *dev, const void *data)
|
|
+{
|
|
+ struct mikrobus_port *port;
|
|
+
|
|
+ if(dev->type != &mikrobus_port_type)
|
|
+ return 0;
|
|
+
|
|
+ port = to_mikrobus_port(dev);
|
|
+
|
|
+ return port->w1_master == data;
|
|
+}
|
|
+
|
|
+struct mikrobus_port *mikrobus_find_port_by_w1_master(struct w1_master *master)
|
|
+{
|
|
+ struct device *dev;
|
|
+
|
|
+ dev = bus_find_device(&mikrobus_bus_type, NULL, master, mikrobus_w1_master_match);
|
|
+ if (!dev)
|
|
+ return NULL;
|
|
+
|
|
+ return (dev->type == &mikrobus_port_type) ? to_mikrobus_port(dev) : NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(mikrobus_find_port_by_w1_master);
|
|
+
|
|
+int mikrobus_port_pinctrl_select(struct mikrobus_port *port)
|
|
+{
|
|
+ struct pinctrl_state *state;
|
|
+ int retval;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < MIKROBUS_NUM_PINCTRL_STATE; i++) {
|
|
+ state = pinctrl_lookup_state(port->pinctrl,
|
|
+ port->pinctrl_selected[i]);
|
|
+ if (!IS_ERR(state)) {
|
|
+ retval = pinctrl_select_state(port->pinctrl, state);
|
|
+ pr_info("setting pinctrl %s\n",
|
|
+ port->pinctrl_selected[i]);
|
|
+ if (retval != 0) {
|
|
+ dev_err(&port->dev, "failed to select state %s\n",
|
|
+ port->pinctrl_selected[i]);
|
|
+ return retval;
|
|
+ }
|
|
+ } else {
|
|
+ dev_err(&port->dev, "failed to find state %s\n",
|
|
+ port->pinctrl_selected[i]);
|
|
+ return PTR_ERR(state);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mikrobus_port_pinctrl_select);
|
|
+
|
|
+static int mikrobus_port_pinctrl_setup(struct mikrobus_port *port, struct addon_board_info *board)
|
|
+{
|
|
+ int retval;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < MIKROBUS_NUM_PINCTRL_STATE; i++) {
|
|
+ switch (i) {
|
|
+ case MIKROBUS_PINCTRL_PWM:
|
|
+ if (board->pin_state[MIKROBUS_PIN_PWM] == MIKROBUS_STATE_PWM)
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], PINCTRL_STATE_DEFAULT);
|
|
+ else
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], MIKROBUS_PINCTRL_STATE_GPIO);
|
|
+ break;
|
|
+ case MIKROBUS_PINCTRL_UART:
|
|
+ if (board->pin_state[MIKROBUS_PIN_RX] == MIKROBUS_STATE_UART)
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], PINCTRL_STATE_DEFAULT);
|
|
+ else
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], MIKROBUS_PINCTRL_STATE_GPIO);
|
|
+ break;
|
|
+ case MIKROBUS_PINCTRL_I2C:
|
|
+ if (board->pin_state[MIKROBUS_PIN_SCL] == MIKROBUS_STATE_I2C)
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], PINCTRL_STATE_DEFAULT);
|
|
+ else
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], MIKROBUS_PINCTRL_STATE_GPIO);
|
|
+ break;
|
|
+ case MIKROBUS_PINCTRL_SPI:
|
|
+ if (board->pin_state[MIKROBUS_PIN_MOSI] == MIKROBUS_STATE_SPI)
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], PINCTRL_STATE_DEFAULT);
|
|
+ else
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], MIKROBUS_PINCTRL_STATE_GPIO);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ retval = mikrobus_port_pinctrl_select(port);
|
|
+ if (retval)
|
|
+ dev_err(&port->dev, "failed to select pinctrl states [%d]", retval);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static int mikrobus_irq_get(struct mikrobus_port *port, int irqno,
|
|
+ int irq_type)
|
|
+{
|
|
+ int irq;
|
|
+
|
|
+ if (irqno > port->gpios->ndescs - 1) {
|
|
+ dev_err(&port->dev, "GPIO %d does not exist", irqno);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ irq = gpiod_to_irq(port->gpios->desc[irqno]);
|
|
+ if (irq < 0) {
|
|
+ dev_err(&port->dev, "could not get irq %d", irqno);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ irq_set_irq_type(irq, irq_type);
|
|
+ return irq;
|
|
+}
|
|
+
|
|
+static int mikrobus_gpio_setup(struct gpio_desc *gpio, int gpio_state)
|
|
+{
|
|
+ int retval;
|
|
+
|
|
+ switch (gpio_state) {
|
|
+ case MIKROBUS_STATE_INPUT:
|
|
+ retval = gpiod_direction_input(gpio);
|
|
+ break;
|
|
+ case MIKROBUS_STATE_OUTPUT_HIGH:
|
|
+ retval = gpiod_direction_output(gpio, 1);
|
|
+ break;
|
|
+ case MIKROBUS_STATE_OUTPUT_LOW:
|
|
+ retval = gpiod_direction_output(gpio, 0);
|
|
+ break;
|
|
+ case MIKROBUS_STATE_PWM:
|
|
+ case MIKROBUS_STATE_SPI:
|
|
+ case MIKROBUS_STATE_I2C:
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static char *mikrobus_gpio_chip_name_get(struct mikrobus_port *port, int gpio)
|
|
+{
|
|
+ char *name;
|
|
+ struct gpio_chip *gpiochip;
|
|
+
|
|
+ if (gpio > port->gpios->ndescs - 1)
|
|
+ return NULL;
|
|
+
|
|
+ gpiochip = gpiod_to_chip(port->gpios->desc[gpio]);
|
|
+ name = kmemdup(gpiochip->label, MIKROBUS_NAME_SIZE, GFP_KERNEL);
|
|
+ return name;
|
|
+}
|
|
+
|
|
+static int mikrobus_gpio_hwnum_get(struct mikrobus_port *port, int gpio)
|
|
+{
|
|
+ int hwnum;
|
|
+ struct gpio_chip *gpiochip;
|
|
+
|
|
+ if (gpio > port->gpios->ndescs - 1)
|
|
+ return -ENODEV;
|
|
+
|
|
+ gpiochip = gpiod_to_chip(port->gpios->desc[gpio]);
|
|
+ hwnum = desc_to_gpio(port->gpios->desc[gpio]) - gpiochip->base;
|
|
+ return hwnum;
|
|
+}
|
|
+
|
|
+static void mikrobus_board_device_release_all(struct addon_board_info *info)
|
|
+{
|
|
+ struct board_device_info *dev;
|
|
+ struct board_device_info *next;
|
|
+
|
|
+ list_for_each_entry_safe(dev, next, &info->devices, links) {
|
|
+ list_del(&dev->links);
|
|
+ kfree(dev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int mikrobus_device_register(struct mikrobus_port *port,
|
|
+ struct board_device_info *dev, char *board_name)
|
|
+{
|
|
+ struct i2c_board_info *i2c;
|
|
+ struct spi_device *spi;
|
|
+ struct serdev_device *serdev;
|
|
+ struct platform_device *pdev;
|
|
+ struct gpiod_lookup_table *lookup;
|
|
+ struct regulator_consumer_supply regulator;
|
|
+ char devname[MIKROBUS_NAME_SIZE];
|
|
+ int i;
|
|
+ u64 *val;
|
|
+
|
|
+ dev_info(&port->dev, "registering device : %s", dev->drv_name);
|
|
+
|
|
+ if (dev->gpio_lookup) {
|
|
+ lookup = dev->gpio_lookup;
|
|
+ if (dev->protocol == GREYBUS_PROTOCOL_SPI) {
|
|
+ snprintf(devname, sizeof(devname), "%s.%u",
|
|
+ dev_name(&port->spi_mstr->dev),
|
|
+ port->chip_select[dev->reg]);
|
|
+ lookup->dev_id = kmemdup(devname, MIKROBUS_NAME_SIZE, GFP_KERNEL);
|
|
+ } else if (dev->protocol == GREYBUS_PROTOCOL_RAW) {
|
|
+ snprintf(devname, sizeof(devname), "%s.%u",
|
|
+ dev->drv_name, dev->reg);
|
|
+ lookup->dev_id = kmemdup(devname, MIKROBUS_NAME_SIZE, GFP_KERNEL);
|
|
+ } else
|
|
+ lookup->dev_id = dev->drv_name;
|
|
+ dev_info(&port->dev, " adding lookup table : %s\n",
|
|
+ lookup->dev_id);
|
|
+ for (i = 0; i < dev->num_gpio_resources; i++) {
|
|
+ lookup->table[i].key =
|
|
+ mikrobus_gpio_chip_name_get(port,
|
|
+ lookup->table[i].chip_hwnum);
|
|
+ lookup->table[i].chip_hwnum =
|
|
+ mikrobus_gpio_hwnum_get(port,
|
|
+ lookup->table[i].chip_hwnum);
|
|
+ }
|
|
+ gpiod_add_lookup_table(lookup);
|
|
+ }
|
|
+ if (dev->regulators) {
|
|
+ if (dev->protocol == GREYBUS_PROTOCOL_SPI) {
|
|
+ snprintf(devname, sizeof(devname), "%s.%u",
|
|
+ dev_name(&port->spi_mstr->dev),
|
|
+ port->chip_select[dev->reg]);
|
|
+ regulator.dev_name = kmemdup(devname, MIKROBUS_NAME_SIZE, GFP_KERNEL);
|
|
+ } else if (dev->protocol == GREYBUS_PROTOCOL_RAW) {
|
|
+ snprintf(devname, sizeof(devname), "%s.%u",
|
|
+ dev->drv_name, dev->reg);
|
|
+ regulator.dev_name = kmemdup(devname, MIKROBUS_NAME_SIZE, GFP_KERNEL);
|
|
+ } else
|
|
+ regulator.dev_name = dev->drv_name;
|
|
+
|
|
+ for (i = 0; i < dev->num_regulators; i++) {
|
|
+ val = dev->regulators[i].value.u64_data;
|
|
+ regulator.supply = kmemdup(dev->regulators[i].name, MIKROBUS_NAME_SIZE, GFP_KERNEL);
|
|
+ dev_info(&port->dev, " adding fixed regulator %llu uv, %s for %s\n",
|
|
+ *val, regulator.supply, regulator.dev_name);
|
|
+ regulator_register_always_on(0, dev->regulators[i].name, ®ulator,
|
|
+ 1, *val);
|
|
+ }
|
|
+ }
|
|
+ switch (dev->protocol) {
|
|
+ case GREYBUS_PROTOCOL_SPI:
|
|
+ spi = spi_alloc_device(port->spi_mstr);
|
|
+ if (!spi)
|
|
+ return -ENOMEM;
|
|
+ strncpy(spi->modalias, dev->drv_name, sizeof(spi->modalias) - 1);
|
|
+ if (dev->irq)
|
|
+ spi->irq = mikrobus_irq_get(port, dev->irq, dev->irq_type);
|
|
+ if (dev->properties)
|
|
+ device_add_properties(&spi->dev, dev->properties);
|
|
+ spi->chip_select = port->chip_select[dev->reg];
|
|
+ spi->max_speed_hz = dev->max_speed_hz;
|
|
+ spi->mode = dev->mode;
|
|
+ if (dev->clocks) {
|
|
+ for (i = 0; i < dev->num_clocks; i++) {
|
|
+ val = dev->clocks[i].value.u64_data;
|
|
+ dev_info(&port->dev, " adding fixed clock %s, %llu hz\n",
|
|
+ dev->clocks[i].name, *val);
|
|
+ //failing: under debug
|
|
+ clk_register_fixed_rate(&spi->dev, dev->clocks[i].name, devname, 0, *val);
|
|
+ }
|
|
+ }
|
|
+ dev->dev_client = (void *) spi_add_device(spi);
|
|
+ break;
|
|
+ case GREYBUS_PROTOCOL_I2C:
|
|
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
|
|
+ if (!i2c)
|
|
+ return -ENOMEM;
|
|
+ strncpy(i2c->type, dev->drv_name, sizeof(i2c->type) - 1);
|
|
+ if (dev->irq)
|
|
+ i2c->irq = mikrobus_irq_get(port, dev->irq, dev->irq_type);
|
|
+ if (dev->properties)
|
|
+ i2c->properties = dev->properties;
|
|
+ i2c->addr = dev->reg;
|
|
+ dev->dev_client = (void *) i2c_new_client_device(port->i2c_adap, i2c);
|
|
+ break;
|
|
+ case GREYBUS_PROTOCOL_RAW:
|
|
+ pdev = platform_device_alloc(dev->drv_name, 0);
|
|
+ if (!pdev)
|
|
+ return -ENOMEM;
|
|
+ if (dev->properties)
|
|
+ platform_device_add_properties(pdev, dev->properties);
|
|
+ dev->dev_client = pdev;
|
|
+ platform_device_add(dev->dev_client);
|
|
+ break;
|
|
+ case GREYBUS_PROTOCOL_UART:
|
|
+ serdev = serdev_device_alloc(port->ser_ctrl);
|
|
+ if (!serdev)
|
|
+ return -ENOMEM;
|
|
+ strncpy(serdev->modalias, dev->drv_name, sizeof(serdev->modalias) - 1);
|
|
+ if (dev->properties)
|
|
+ device_add_properties(&serdev->dev, dev->properties);
|
|
+ dev->dev_client = serdev;
|
|
+ serdev_device_add(serdev);
|
|
+ break;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void mikrobus_device_unregister(struct mikrobus_port *port,
|
|
+ struct board_device_info *dev, char *board_name)
|
|
+{
|
|
+ dev_info(&port->dev, "removing device %s\n", dev->drv_name);
|
|
+ if (dev->gpio_lookup) {
|
|
+ gpiod_remove_lookup_table(dev->gpio_lookup);
|
|
+ kfree(dev->gpio_lookup);
|
|
+ }
|
|
+ kfree(dev->properties);
|
|
+ switch (dev->protocol) {
|
|
+ case GREYBUS_PROTOCOL_SPI:
|
|
+ spi_unregister_device((struct spi_device *)dev->dev_client);
|
|
+ break;
|
|
+ case GREYBUS_PROTOCOL_I2C:
|
|
+ i2c_unregister_device((struct i2c_client *)dev->dev_client);
|
|
+ break;
|
|
+ case GREYBUS_PROTOCOL_RAW:
|
|
+ platform_device_unregister((struct platform_device *)dev->dev_client);
|
|
+ break;
|
|
+ case GREYBUS_PROTOCOL_UART:
|
|
+ serdev_device_remove((struct serdev_device *)dev->dev_client);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+int mikrobus_board_register(struct mikrobus_port *port, struct addon_board_info *board)
|
|
+{
|
|
+ struct board_device_info *devinfo;
|
|
+ struct board_device_info *next;
|
|
+ int retval;
|
|
+ int i;
|
|
+
|
|
+ if (WARN_ON(list_empty(&board->devices)))
|
|
+ return false;
|
|
+ if (port->pinctrl) {
|
|
+ retval = mikrobus_port_pinctrl_setup(port, board);
|
|
+ if (retval)
|
|
+ dev_err(&port->dev, "failed to setup pinctrl state [%d]", retval);
|
|
+
|
|
+ }
|
|
+ if (port->gpios) {
|
|
+ for (i = 0; i < port->gpios->ndescs; i++) {
|
|
+ retval = mikrobus_gpio_setup(port->gpios->desc[i], board->pin_state[i]);
|
|
+ if (retval)
|
|
+ dev_err(&port->dev, "failed to setup gpio %d, state %d",
|
|
+ i, board->pin_state[i]);
|
|
+ }
|
|
+ }
|
|
+ list_for_each_entry_safe(devinfo, next, &board->devices, links)
|
|
+ mikrobus_device_register(port, devinfo, board->name);
|
|
+ port->board = board;
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mikrobus_board_register);
|
|
+
|
|
+void mikrobus_board_unregister(struct mikrobus_port *port, struct addon_board_info *board)
|
|
+{
|
|
+ struct board_device_info *devinfo;
|
|
+ struct board_device_info *next;
|
|
+
|
|
+ if (WARN_ON(list_empty(&board->devices)))
|
|
+ return;
|
|
+ port->board = NULL;
|
|
+ list_for_each_entry_safe(devinfo, next, &board->devices, links)
|
|
+ mikrobus_device_unregister(port, devinfo, board->name);
|
|
+ mikrobus_board_device_release_all(board);
|
|
+ kfree(board);
|
|
+ port->board = NULL;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mikrobus_board_unregister);
|
|
+
|
|
+static struct w1_gpio_platform_data mikrobus_id_eeprom_w1_pdata = {
|
|
+ .pullup_gpiod = NULL,
|
|
+};
|
|
+
|
|
+static struct platform_device mikrobus_id_eeprom_w1_device = {
|
|
+ .name = "w1-gpio",
|
|
+ .id = -1,
|
|
+ .dev.platform_data = &mikrobus_id_eeprom_w1_pdata,
|
|
+};
|
|
+
|
|
+static int mikrobus_port_id_eeprom_probe(struct mikrobus_port *port)
|
|
+{
|
|
+ struct w1_bus_master *bm;
|
|
+ struct gpiod_lookup_table *lookup;
|
|
+ int retval;
|
|
+ int i;
|
|
+
|
|
+ sprintf(port->pinctrl_selected[MIKROBUS_PINCTRL_SPI], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[MIKROBUS_PINCTRL_SPI], MIKROBUS_PINCTRL_STATE_GPIO);
|
|
+
|
|
+ retval = mikrobus_port_pinctrl_select(port);
|
|
+ /* set MOSI LOW, SCK HIGH */
|
|
+ gpiod_direction_output(port->gpios->desc[MIKROBUS_PIN_MOSI], 0);
|
|
+ gpiod_direction_output(port->gpios->desc[MIKROBUS_PIN_SCK], 1);
|
|
+ msleep(100);
|
|
+ for( i = 0; i < 4; i++){
|
|
+ gpiod_set_value(port->gpios->desc[MIKROBUS_PIN_MOSI] , 1);
|
|
+ udelay(1000);
|
|
+ gpiod_set_value(port->gpios->desc[MIKROBUS_PIN_MOSI] , 0);
|
|
+ udelay(1000);
|
|
+ }
|
|
+ msleep(100); /* temporary delay to fix ROM ID copy */
|
|
+
|
|
+ lookup = kzalloc(struct_size(lookup, table, 1),
|
|
+ GFP_KERNEL);
|
|
+ if (!lookup)
|
|
+ return -ENOMEM;
|
|
+ lookup->dev_id = mikrobus_id_eeprom_w1_device.name;
|
|
+ lookup->table[0].key = mikrobus_gpio_chip_name_get(port,
|
|
+ MIKROBUS_PIN_CS);
|
|
+ lookup->table[0].flags = GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN;
|
|
+ lookup->table[0].chip_hwnum = mikrobus_gpio_hwnum_get(port,
|
|
+ MIKROBUS_PIN_CS);
|
|
+ gpiod_add_lookup_table(lookup);
|
|
+ platform_device_register(&mikrobus_id_eeprom_w1_device);
|
|
+ port->w1_gpio = &mikrobus_id_eeprom_w1_device;
|
|
+ bm = (struct w1_bus_master *) platform_get_drvdata(&mikrobus_id_eeprom_w1_device);
|
|
+ port->w1_master = w1_find_master_device(bm);
|
|
+ mutex_lock(&port->w1_master->mutex);
|
|
+ port->w1_master->max_slave_count = 1;
|
|
+ clear_bit(W1_WARN_MAX_COUNT, &port->w1_master->flags);
|
|
+ mutex_unlock(&port->w1_master->mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int mikrobus_port_register(struct mikrobus_port *port)
|
|
+{
|
|
+ struct device *dev = &port->dev;
|
|
+ int retval;
|
|
+ int id;
|
|
+
|
|
+ if (WARN_ON(!is_registered))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ if (dev->of_node) {
|
|
+ id = of_alias_get_id(dev->of_node, "mikrobus");
|
|
+ if (id >= 0) {
|
|
+ port->id = id;
|
|
+ mutex_lock(&core_lock);
|
|
+ id = idr_alloc(&mikrobus_port_idr, port, port->id, port->id + 1,
|
|
+ GFP_KERNEL);
|
|
+ mutex_unlock(&core_lock);
|
|
+ if (WARN(id < 0, "couldn't get idr"))
|
|
+ return id == -ENOSPC ? -EBUSY : id;
|
|
+ }
|
|
+ } else {
|
|
+ mutex_lock(&core_lock);
|
|
+ id = idr_alloc(&mikrobus_port_idr, port, __mikrobus_first_dynamic_bus_num, 0,
|
|
+ GFP_KERNEL);
|
|
+ mutex_unlock(&core_lock);
|
|
+ if (id < 0)
|
|
+ return id;
|
|
+ port->id = id;
|
|
+ }
|
|
+ port->dev.bus = &mikrobus_bus_type;
|
|
+ port->dev.type = &mikrobus_port_type;
|
|
+ strncpy(port->name, "mikrobus-port", sizeof(port->name) - 1);
|
|
+ dev_set_name(&port->dev, "mikrobus-%d", port->id);
|
|
+ pr_info("registering port mikrobus-%d ", port->id);
|
|
+ retval = device_register(&port->dev);
|
|
+ if (retval) {
|
|
+ pr_err("port '%d': can't register device (%d)", port->id, retval);
|
|
+ put_device(&port->dev);
|
|
+ return retval;
|
|
+ }
|
|
+ retval = class_compat_create_link(mikrobus_port_compat_class, &port->dev,
|
|
+ port->dev.parent);
|
|
+ if (retval)
|
|
+ dev_warn(&port->dev, "failed to create compatibility class link\n");
|
|
+ if (!port->w1_master) {
|
|
+ dev_info(&port->dev, "mikrobus port %d eeprom empty probing default eeprom\n",
|
|
+ port->id);
|
|
+ retval = mikrobus_port_id_eeprom_probe(port);
|
|
+ }
|
|
+ // if (port->w1_master) {
|
|
+ // retval = mikrobus_port_scan_eeprom(port);
|
|
+ // if (retval) {
|
|
+ // dev_warn(&port->dev, "failed to register board from manifest\n");
|
|
+ // return 0;
|
|
+ // }
|
|
+ // }
|
|
+ return retval;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mikrobus_port_register);
|
|
+
|
|
+void mikrobus_port_delete(struct mikrobus_port *port)
|
|
+{
|
|
+ struct mikrobus_port *found;
|
|
+
|
|
+ mutex_lock(&core_lock);
|
|
+ found = idr_find(&mikrobus_port_idr, port->id);
|
|
+ mutex_unlock(&core_lock);
|
|
+ if (found != port) {
|
|
+ pr_err("port [%s] not registered", port->name);
|
|
+ return;
|
|
+ }
|
|
+ if (port->board) {
|
|
+ dev_err(&port->dev, "attempting to delete port with registered boards, port [%s]\n",
|
|
+ port->name);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (port->eeprom) {
|
|
+ nvmem_device_put(port->eeprom);
|
|
+ platform_device_unregister(port->w1_gpio);
|
|
+ }
|
|
+
|
|
+ class_compat_remove_link(mikrobus_port_compat_class, &port->dev,
|
|
+ port->dev.parent);
|
|
+ device_unregister(&port->dev);
|
|
+ mutex_lock(&core_lock);
|
|
+ idr_remove(&mikrobus_port_idr, port->id);
|
|
+ mutex_unlock(&core_lock);
|
|
+ memset(&port->dev, 0, sizeof(port->dev));
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mikrobus_port_delete);
|
|
+
|
|
+static int mikrobus_port_probe_pinctrl_setup(struct mikrobus_port *port)
|
|
+{
|
|
+ struct pinctrl_state *state;
|
|
+ struct device *dev = port->dev.parent;
|
|
+ int retval, i;
|
|
+
|
|
+ state = pinctrl_lookup_state(port->pinctrl, PINCTRL_STATE_DEFAULT);
|
|
+ if (!IS_ERR(state)) {
|
|
+ retval = pinctrl_select_state(port->pinctrl, state);
|
|
+ if (retval != 0) {
|
|
+ dev_err(dev, "Failed to select state %s\n",
|
|
+ PINCTRL_STATE_DEFAULT);
|
|
+ return retval;
|
|
+ }
|
|
+ } else {
|
|
+ dev_err(dev, "failed to find state %s\n",
|
|
+ PINCTRL_STATE_DEFAULT);
|
|
+ return PTR_ERR(state);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < MIKROBUS_NUM_PINCTRL_STATE; i++) {
|
|
+ port->pinctrl_selected[i] =
|
|
+ kmalloc(MIKROBUS_PINCTRL_NAME_SIZE, GFP_KERNEL);
|
|
+ sprintf(port->pinctrl_selected[i], "%s_%s",
|
|
+ MIKROBUS_PINCTRL_STR[i], PINCTRL_STATE_DEFAULT);
|
|
+ }
|
|
+
|
|
+ retval = mikrobus_port_pinctrl_select(port);
|
|
+ if (retval)
|
|
+ dev_err(dev, "failed to select pinctrl states [%d]", retval);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static int mikrobus_port_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct mikrobus_port *port;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *i2c_adap_np;
|
|
+ struct device_node *uart_np;
|
|
+ int retval;
|
|
+ u32 val;
|
|
+
|
|
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
|
|
+ if (!port)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ i2c_adap_np = of_parse_phandle(dev->of_node, "i2c-adapter", 0);
|
|
+ if (!i2c_adap_np) {
|
|
+ dev_err(dev, "cannot parse i2c-adapter\n");
|
|
+ retval = -ENODEV;
|
|
+ goto err_port;
|
|
+ }
|
|
+ port->i2c_adap = of_find_i2c_adapter_by_node(i2c_adap_np);
|
|
+ of_node_put(i2c_adap_np);
|
|
+ retval = device_property_read_u32(dev, "spi-master", &val);
|
|
+ if (retval) {
|
|
+ dev_err(dev, "failed to get spi-master [%d]\n", retval);
|
|
+ goto err_port;
|
|
+ }
|
|
+ port->spi_mstr = spi_busnum_to_master(val);
|
|
+ retval = device_property_read_u32_array(dev, "spi-cs",
|
|
+ port->chip_select, 2);
|
|
+ if (retval) {
|
|
+ dev_err(dev, "failed to get spi-cs [%d]\n", retval);
|
|
+ goto err_port;
|
|
+ }
|
|
+ uart_np = of_parse_phandle(dev->of_node, "uart", 0);
|
|
+ if (!uart_np) {
|
|
+ dev_err(dev, "cannot parse uart\n");
|
|
+ retval = -ENODEV;
|
|
+ goto err_port;
|
|
+ }
|
|
+ port->ser_ctrl = of_find_serdev_controller_by_node(uart_np);
|
|
+ of_node_put(uart_np);
|
|
+ //port->pwm = devm_pwm_get(dev, NULL);
|
|
+ //if (IS_ERR(port->pwm)) {
|
|
+ // retval = PTR_ERR(port->pwm);
|
|
+ // if (retval != -EPROBE_DEFER)
|
|
+ // dev_err(dev, "failed to request PWM device [%d]\n",
|
|
+ // retval);
|
|
+ // goto err_port;
|
|
+ //}
|
|
+ port->gpios = gpiod_get_array(dev, "mikrobus", GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(port->gpios)) {
|
|
+ retval = PTR_ERR(port->gpios);
|
|
+ dev_err(dev, "failed to get gpio array [%d]\n", retval);
|
|
+ goto err_port;
|
|
+ }
|
|
+ port->pinctrl = devm_pinctrl_get(dev);
|
|
+ if (IS_ERR(port->pinctrl)) {
|
|
+ retval = PTR_ERR(port->pinctrl);
|
|
+ dev_err(dev, "failed to get pinctrl [%d]\n", retval);
|
|
+ goto err_port;
|
|
+ }
|
|
+ port->dev.parent = dev;
|
|
+ port->dev.of_node = pdev->dev.of_node;
|
|
+
|
|
+ retval = mikrobus_port_probe_pinctrl_setup(port);
|
|
+ if (retval) {
|
|
+ dev_err(dev, "failed to setup pinctrl [%d]\n", retval);
|
|
+ goto err_port;
|
|
+ }
|
|
+
|
|
+ retval = mikrobus_port_register(port);
|
|
+ if (retval) {
|
|
+ pr_err("port : can't register port [%d]\n", retval);
|
|
+ goto err_port;
|
|
+ }
|
|
+ platform_set_drvdata(pdev, port);
|
|
+ return 0;
|
|
+err_port:
|
|
+ kfree(port);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static int mikrobus_port_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct mikrobus_port *port = platform_get_drvdata(pdev);
|
|
+
|
|
+ mikrobus_port_delete(port);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id mikrobus_port_of_match[] = {
|
|
+ {.compatible = "linux,mikrobus"},
|
|
+ {},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, mikrobus_port_of_match);
|
|
+
|
|
+static struct platform_driver mikrobus_port_driver = {
|
|
+ .probe = mikrobus_port_probe,
|
|
+ .remove = mikrobus_port_remove,
|
|
+ .driver = {
|
|
+ .name = "mikrobus",
|
|
+ .of_match_table = of_match_ptr(mikrobus_port_of_match),
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init mikrobus_init(void)
|
|
+{
|
|
+ int retval;
|
|
+
|
|
+ retval = bus_register(&mikrobus_bus_type);
|
|
+ if (retval) {
|
|
+ pr_err("bus_register failed (%d)\n", retval);
|
|
+ return retval;
|
|
+ }
|
|
+ mikrobus_port_compat_class = class_compat_register("mikrobus-port");
|
|
+ if (!mikrobus_port_compat_class) {
|
|
+ pr_err("class_compat register failed (%d)\n", retval);
|
|
+ retval = -ENOMEM;
|
|
+ goto class_err;
|
|
+ }
|
|
+ retval = of_alias_get_highest_id("mikrobus");
|
|
+ if (retval >= __mikrobus_first_dynamic_bus_num)
|
|
+ __mikrobus_first_dynamic_bus_num = retval + 1;
|
|
+
|
|
+ is_registered = true;
|
|
+ retval = platform_driver_register(&mikrobus_port_driver);
|
|
+ if (retval)
|
|
+ pr_err("driver register failed [%d]\n", retval);
|
|
+ return retval;
|
|
+
|
|
+class_err:
|
|
+ bus_unregister(&mikrobus_bus_type);
|
|
+ idr_destroy(&mikrobus_port_idr);
|
|
+ is_registered = false;
|
|
+ return retval;
|
|
+}
|
|
+subsys_initcall(mikrobus_init);
|
|
+
|
|
+static void __exit mikrobus_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&mikrobus_port_driver);
|
|
+ bus_unregister(&mikrobus_bus_type);
|
|
+ class_compat_unregister(mikrobus_port_compat_class);
|
|
+ idr_destroy(&mikrobus_port_idr);
|
|
+}
|
|
+module_exit(mikrobus_exit);
|
|
+
|
|
+MODULE_AUTHOR("Vaishnav M A <vaishnav@beagleboard.org>");
|
|
+MODULE_DESCRIPTION("mikroBUS main module");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/misc/mikrobus/mikrobus_core.h b/drivers/misc/mikrobus/mikrobus_core.h
|
|
new file mode 100644
|
|
index 000000000000..f7c53d760a12
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/mikrobus_core.h
|
|
@@ -0,0 +1,201 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/*
|
|
+ * mikroBUS Driver for instantiating add-on
|
|
+ * board devices with an identifier EEPROM
|
|
+ *
|
|
+ * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation.
|
|
+ */
|
|
+
|
|
+#ifndef __MIKROBUS_H
|
|
+#define __MIKROBUS_H
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/gpio/machine.h>
|
|
+#include <linux/spi/spi.h>
|
|
+#include <linux/serdev.h>
|
|
+#include <linux/property.h>
|
|
+#include <linux/pinctrl/pinctrl.h>
|
|
+#include <linux/pinctrl/pinmux.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
+#include <linux/nvmem-consumer.h>
|
|
+#include <linux/nvmem-provider.h>
|
|
+
|
|
+#define MIKROBUS_VERSION_MAJOR 0x00
|
|
+#define MIKROBUS_VERSION_MINOR 0x03
|
|
+
|
|
+#define MIKROBUS_NAME_SIZE 40
|
|
+#define MIKROBUS_PINCTRL_NAME_SIZE 20
|
|
+
|
|
+#define MIKROBUS_NUM_PINCTRL_STATE 4
|
|
+#define MIKROBUS_NUM_CS 2
|
|
+
|
|
+#define MIKROBUS_PINCTRL_PWM 0
|
|
+#define MIKROBUS_PINCTRL_UART 1
|
|
+#define MIKROBUS_PINCTRL_I2C 2
|
|
+#define MIKROBUS_PINCTRL_SPI 3
|
|
+
|
|
+#define MIKROBUS_PINCTRL_STATE_GPIO "gpio"
|
|
+
|
|
+#define MIKROBUS_EEPROM_EXIT_ID_CMD 0xD2
|
|
+
|
|
+extern struct bus_type mikrobus_bus_type;
|
|
+extern struct device_type mikrobus_port_type;
|
|
+extern const char *MIKROBUS_PINCTRL_STR[];
|
|
+
|
|
+enum mikrobus_property_type {
|
|
+ MIKROBUS_PROPERTY_TYPE_MIKROBUS = 0x00,
|
|
+ MIKROBUS_PROPERTY_TYPE_PROPERTY = 0x01,
|
|
+ MIKROBUS_PROPERTY_TYPE_GPIO = 0x02,
|
|
+ MIKROBUS_PROPERTY_TYPE_U8 = 0x03,
|
|
+ MIKROBUS_PROPERTY_TYPE_U16 = 0x04,
|
|
+ MIKROBUS_PROPERTY_TYPE_U32 = 0x05,
|
|
+ MIKROBUS_PROPERTY_TYPE_U64 = 0x06,
|
|
+ MIKROBUS_PROPERTY_TYPE_REGULATOR = 0x07,
|
|
+ MIKROBUS_PROPERTY_TYPE_CLOCK = 0x08,
|
|
+};
|
|
+
|
|
+enum mikrobus_pin {
|
|
+ MIKROBUS_PIN_PWM = 0x00,
|
|
+ MIKROBUS_PIN_INT = 0x01,
|
|
+ MIKROBUS_PIN_RX = 0x02,
|
|
+ MIKROBUS_PIN_TX = 0x03,
|
|
+ MIKROBUS_PIN_SCL = 0x04,
|
|
+ MIKROBUS_PIN_SDA = 0x05,
|
|
+ MIKROBUS_PIN_MOSI = 0x06,
|
|
+ MIKROBUS_PIN_MISO = 0x07,
|
|
+ MIKROBUS_PIN_SCK = 0x08,
|
|
+ MIKROBUS_PIN_CS = 0x09,
|
|
+ MIKROBUS_PIN_RST = 0x0A,
|
|
+ MIKROBUS_PIN_AN = 0x0B,
|
|
+ MIKROBUS_PORT_PIN_COUNT = 0x0C,
|
|
+};
|
|
+
|
|
+enum mikrobus_pin_state {
|
|
+ MIKROBUS_STATE_INPUT = 0x01,
|
|
+ MIKROBUS_STATE_OUTPUT_HIGH = 0x02,
|
|
+ MIKROBUS_STATE_OUTPUT_LOW = 0x03,
|
|
+ MIKROBUS_STATE_PWM = 0x04,
|
|
+ MIKROBUS_STATE_SPI = 0x05,
|
|
+ MIKROBUS_STATE_I2C = 0x06,
|
|
+ MIKROBUS_STATE_UART = 0x07,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * board_device_info describes a single device on a mikrobus add-on
|
|
+ * board, an add-on board can present one or more device to the host
|
|
+ *
|
|
+ * @gpio_lookup: used to provide the GPIO lookup table for
|
|
+ * passing the named GPIOs to device drivers.
|
|
+ * @properties: used to provide the property_entry to pass named
|
|
+ * properties to device drivers, applicable only when driver uses
|
|
+ * device_property_read_* calls to fetch the properties.
|
|
+ * @num_gpio_resources: number of named gpio resources for the device,
|
|
+ * used mainly for gpiod_lookup_table memory allocation.
|
|
+ * @num_properties: number of custom properties for the device,
|
|
+ * used mainly for property_entry memory allocation.
|
|
+ * @protocol: used to know the type of the device and it should
|
|
+ * contain one of the values defined under 'enum greybus_class_type'
|
|
+ * under linux/greybus/greybus_manifest.h
|
|
+ * @reg: I2C address for the device, for devices on the SPI bus
|
|
+ * this field is the chip select address relative to the mikrobus
|
|
+ * port:0->device chip select connected to CS pin on mikroBUS port
|
|
+ * 1->device chip select connected to RST Pin on mikroBUS port
|
|
+ * @mode: SPI mode
|
|
+ * @max_speed_hz: SPI max speed(Hz)
|
|
+ * @drv_name: device_id to match with the driver
|
|
+ * @irq_type: type of IRQ trigger , match with defines in linux/interrupt.h
|
|
+ * @irq: irq number relative to the mikrobus port should contain one of the
|
|
+ * values defined under 'enum mikrobus_pin'
|
|
+ * @id: device id starting from 1
|
|
+ */
|
|
+struct board_device_info {
|
|
+ struct gpiod_lookup_table *gpio_lookup;
|
|
+ struct property_entry *properties;
|
|
+ struct property_entry *regulators;
|
|
+ struct property_entry *clocks;
|
|
+ struct list_head links;
|
|
+ unsigned short num_gpio_resources;
|
|
+ unsigned short num_properties;
|
|
+ unsigned short num_regulators;
|
|
+ unsigned short num_clocks;
|
|
+ unsigned short protocol;
|
|
+ unsigned short reg;
|
|
+ unsigned int mode;
|
|
+ void *dev_client;
|
|
+ u32 max_speed_hz;
|
|
+ char *drv_name;
|
|
+ int irq_type;
|
|
+ int irq;
|
|
+ int id;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * addon_board_info describes a mikrobus add-on device the add-on
|
|
+ * board, an add-on board can present one or more device to the host
|
|
+ *
|
|
+ * @manifest_descs: list of manifest descriptors
|
|
+ * @devices: list of devices on the board
|
|
+ * @pin_state: the state of each pin on the mikrobus port required
|
|
+ * for the add-on board should contain one of the values defined under
|
|
+ * 'enum mikrobus_pin_state' restrictions are as per mikrobus standard
|
|
+ * specifications.
|
|
+ * @name: add-on board name
|
|
+ */
|
|
+struct addon_board_info {
|
|
+ struct list_head manifest_descs;
|
|
+ struct list_head devices;
|
|
+ u8 pin_state[MIKROBUS_PORT_PIN_COUNT];
|
|
+ char *name;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * mikrobus_port describes the peripherals mapped to a
|
|
+ * mikrobus port.
|
|
+ *
|
|
+ * @eeprom_client: i2c_client corresponding to the eeprom
|
|
+ * on the add-on board.
|
|
+ * @board: pointer to the attached add-on board.
|
|
+ * @i2c_adap: I2C adapter attached to the mikrobus port.
|
|
+ * @spi_mstr: SPI master attached to the mikrobus port.
|
|
+ * @eeprom: nvmem_device for the eeprom on the add-on board.
|
|
+ * @pwm: pwm_device attached to the mikrobus port PWM pin.
|
|
+ * @pinctrl_selected: current pinctrl_selected state.
|
|
+ * @chip_select: chip select number mapped to the SPI
|
|
+ * CS pin on the mikrobus port and the RST pin on the mikrobus
|
|
+ * port
|
|
+ * @id: port id starting from 1
|
|
+ */
|
|
+struct mikrobus_port {
|
|
+ struct addon_board_info *board;
|
|
+ struct nvmem_device *eeprom;
|
|
+ struct i2c_adapter *i2c_adap;
|
|
+ struct spi_master *spi_mstr;
|
|
+ struct w1_master *w1_master;
|
|
+ struct platform_device *w1_gpio;
|
|
+ struct serdev_controller *ser_ctrl;
|
|
+ struct gpio_descs *gpios;
|
|
+ struct pwm_device *pwm;
|
|
+ struct pinctrl *pinctrl;
|
|
+ struct module *owner;
|
|
+ struct device dev;
|
|
+ char name[MIKROBUS_NAME_SIZE];
|
|
+ char *pinctrl_selected[MIKROBUS_NUM_PINCTRL_STATE];
|
|
+ unsigned int chip_select[MIKROBUS_NUM_CS];
|
|
+ int id;
|
|
+};
|
|
+#define to_mikrobus_port(d) container_of(d, struct mikrobus_port, dev)
|
|
+
|
|
+void mikrobus_board_unregister(struct mikrobus_port *port,
|
|
+ struct addon_board_info *board);
|
|
+int mikrobus_board_register(struct mikrobus_port *port,
|
|
+ struct addon_board_info *board);
|
|
+int mikrobus_port_register(struct mikrobus_port *port);
|
|
+int mikrobus_port_pinctrl_select(struct mikrobus_port *port);
|
|
+void mikrobus_port_delete(struct mikrobus_port *port);
|
|
+int mikrobus_port_scan_eeprom(struct mikrobus_port *port);
|
|
+struct mikrobus_port *mikrobus_find_port_by_w1_master(struct w1_master *master);
|
|
+#endif /* __MIKROBUS_H */
|
|
diff --git a/drivers/misc/mikrobus/mikrobus_id.c b/drivers/misc/mikrobus/mikrobus_id.c
|
|
new file mode 100644
|
|
index 000000000000..e891645f745c
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/mikrobus_id.c
|
|
@@ -0,0 +1,207 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * mikrobus_id.c - w1 mikroBUS ID family EEPROM driver
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include <linux/w1.h>
|
|
+#include <linux/nvmem-provider.h>
|
|
+
|
|
+#include "mikrobus_core.h"
|
|
+
|
|
+#define W1_EEPROM_MIKROBUS_ID 0xAC
|
|
+
|
|
+#define W1_MIKROBUS_ID_EEPROM_SIZE 1536
|
|
+#define W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE 32
|
|
+#define W1_MIKROBUS_ID_EEPROM_VERIFY_SCRATCH_SIZE 35
|
|
+#define W1_MIKROBUS_ID_READ_EEPROM 0xF0
|
|
+#define W1_MIKROBUS_ID_EEPROM_READ_RETRIES 10
|
|
+#define W1_MIKROBUS_ID_EEPROM_WRITE_RETRIES 5
|
|
+#define W1_MIKROBUS_ID_EEPROM_WRITE_SCRATCH 0x0F
|
|
+#define W1_MIKROBUS_ID_EEPROM_READ_SCRATCH 0xAA
|
|
+#define W1_MIKROBUS_ID_EEPROM_COPY_SCRATCH 0x55
|
|
+#define W1_MIKROBUS_ID_EEPROM_COPY_SCRATCH_ES 0x40
|
|
+#define W1_MIKROBUS_ID_EEPROM_TPROG_MS 20
|
|
+
|
|
+static int w1_mikrobus_id_readblock(struct w1_slave *sl, int off, int count, char *buf)
|
|
+{
|
|
+ u8 wrbuf[3];
|
|
+ u8 *cmp;
|
|
+ int tries = W1_MIKROBUS_ID_EEPROM_READ_RETRIES;
|
|
+
|
|
+ do {
|
|
+ wrbuf[0] = W1_MIKROBUS_ID_READ_EEPROM;
|
|
+ wrbuf[1] = count >> 8;
|
|
+ wrbuf[2] = count & 0xFF;
|
|
+
|
|
+ if (w1_reset_select_slave(sl))
|
|
+ return -1;
|
|
+ w1_write_block(sl->master, wrbuf, 3);
|
|
+ w1_read_block(sl->master, buf, count);
|
|
+
|
|
+ if (w1_reset_select_slave(sl))
|
|
+ return -1;
|
|
+ cmp = kzalloc(count, GFP_KERNEL);
|
|
+ if (!cmp)
|
|
+ return -ENOMEM;
|
|
+ w1_write_block(sl->master, wrbuf, 3);
|
|
+ w1_read_block(sl->master, cmp, count);
|
|
+ if (!memcmp(cmp, buf, count)){
|
|
+ kfree(cmp);
|
|
+ return 0;
|
|
+ }
|
|
+ } while (--tries);
|
|
+
|
|
+ dev_err(&sl->dev, "proof reading failed %d times\n",
|
|
+ W1_MIKROBUS_ID_EEPROM_READ_RETRIES);
|
|
+ kfree(cmp);
|
|
+ return -EIO;
|
|
+}
|
|
+
|
|
+static int w1_mikrobus_id_movescratch(struct w1_slave *sl, int addr, char *buf)
|
|
+{
|
|
+ u8 wrbuf[4];
|
|
+ u8 scratchpad_verify[W1_MIKROBUS_ID_EEPROM_VERIFY_SCRATCH_SIZE];
|
|
+ u8 write_scratchpad_crc[2];
|
|
+ int verify_status;
|
|
+ int tries;
|
|
+
|
|
+ wrbuf[0] = W1_MIKROBUS_ID_EEPROM_WRITE_SCRATCH;
|
|
+ wrbuf[1] = addr >> 8;
|
|
+ wrbuf[2] = addr & 0xFF;
|
|
+
|
|
+ tries = W1_MIKROBUS_ID_EEPROM_WRITE_RETRIES;
|
|
+ do {
|
|
+ if (w1_reset_select_slave(sl))
|
|
+ return -1;
|
|
+ w1_write_block(sl->master, wrbuf, 3);
|
|
+ w1_write_block(sl->master, buf, W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE);
|
|
+ udelay(100); /* delay for CRC calculation at slave */
|
|
+ w1_read_block(sl->master, write_scratchpad_crc, 2);
|
|
+ if (w1_reset_select_slave(sl))
|
|
+ return -1;
|
|
+ w1_write_8(sl->master, W1_MIKROBUS_ID_EEPROM_READ_SCRATCH);
|
|
+ w1_read_block(sl->master, scratchpad_verify, W1_MIKROBUS_ID_EEPROM_VERIFY_SCRATCH_SIZE);
|
|
+ verify_status = memcmp(buf, scratchpad_verify + 3, W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE);
|
|
+ } while(verify_status && --tries);
|
|
+
|
|
+ if(!tries && verify_status){
|
|
+ dev_err(&sl->dev, "verify scratchpad failed %d times\n",
|
|
+ W1_MIKROBUS_ID_EEPROM_WRITE_RETRIES);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ wrbuf[0] = W1_MIKROBUS_ID_EEPROM_COPY_SCRATCH;
|
|
+ wrbuf[1] = addr >> 8;
|
|
+ wrbuf[2] = addr & 0xFF;
|
|
+ wrbuf[3] = W1_MIKROBUS_ID_EEPROM_COPY_SCRATCH_ES;
|
|
+ if (w1_reset_select_slave(sl))
|
|
+ return -1;
|
|
+ w1_write_block(sl->master, wrbuf, 4);
|
|
+ msleep(W1_MIKROBUS_ID_EEPROM_TPROG_MS);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int w1_mikrobus_id_writeblock(struct w1_slave *sl, int off, int count, char *buf)
|
|
+{
|
|
+ u16 wraddr = 0;
|
|
+ u16 len = count - (count % W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE);
|
|
+ u8 scratchpad_write[W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE];
|
|
+
|
|
+ while(len > 0) {
|
|
+ w1_mikrobus_id_movescratch(sl, wraddr, buf + wraddr);
|
|
+ wraddr += W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE;
|
|
+ len -= W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE;
|
|
+ }
|
|
+
|
|
+ if(count % W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE){
|
|
+ memcpy(scratchpad_write, buf + wraddr, count % W1_MIKROBUS_ID_EEPROM_SCRATCH_SIZE);
|
|
+ w1_mikrobus_id_movescratch(sl, wraddr, scratchpad_write);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int w1_mikrobus_id_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)
|
|
+{
|
|
+ struct w1_slave *sl = priv;
|
|
+ int ret;
|
|
+
|
|
+ /* mikroBUS ID EEPROM does not support reading from offsets */
|
|
+ if (off)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (count > W1_MIKROBUS_ID_EEPROM_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&sl->master->bus_mutex);
|
|
+ ret = w1_mikrobus_id_readblock(sl, off, count, buf);
|
|
+ mutex_unlock(&sl->master->bus_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int w1_mikrobus_id_nvmem_write(void *priv, unsigned int off, void *buf, size_t count)
|
|
+{
|
|
+ struct w1_slave *sl = priv;
|
|
+ int ret;
|
|
+
|
|
+ if ((off + count) > W1_MIKROBUS_ID_EEPROM_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&sl->master->bus_mutex);
|
|
+ ret = w1_mikrobus_id_writeblock(sl, off, count, buf);
|
|
+ mutex_unlock(&sl->master->bus_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int w1_mikrobus_id_add_slave(struct w1_slave *sl)
|
|
+{
|
|
+ struct nvmem_device *nvmem;
|
|
+ struct mikrobus_port *port;
|
|
+ struct nvmem_config nvmem_cfg = {
|
|
+ .dev = &sl->dev,
|
|
+ .reg_read = w1_mikrobus_id_nvmem_read,
|
|
+ .reg_write = w1_mikrobus_id_nvmem_write,
|
|
+ .type = NVMEM_TYPE_EEPROM,
|
|
+ .read_only = false,
|
|
+ .word_size = 1,
|
|
+ .stride = 1,
|
|
+ .size = W1_MIKROBUS_ID_EEPROM_SIZE,
|
|
+ .priv = sl,
|
|
+ };
|
|
+
|
|
+ port = mikrobus_find_port_by_w1_master(sl->master);
|
|
+ if(!port)
|
|
+ return -ENODEV;
|
|
+
|
|
+ nvmem_cfg.name = port->name;
|
|
+ nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);
|
|
+ port->eeprom = nvmem;
|
|
+ mikrobus_port_scan_eeprom(port);
|
|
+
|
|
+ return PTR_ERR_OR_ZERO(nvmem);
|
|
+}
|
|
+
|
|
+static const struct w1_family_ops w1_family_mikrobus_id_fops = {
|
|
+ .add_slave = w1_mikrobus_id_add_slave,
|
|
+};
|
|
+
|
|
+static struct w1_family w1_family_mikrobus_id = {
|
|
+ .fid = W1_EEPROM_MIKROBUS_ID,
|
|
+ .fops = &w1_family_mikrobus_id_fops,
|
|
+};
|
|
+module_w1_family(w1_family_mikrobus_id);
|
|
+
|
|
+MODULE_AUTHOR("Vaishnav M A <vaishnav@beagleboard.org>");
|
|
+MODULE_DESCRIPTION("w1 family ac driver for mikroBUS ID EEPROM");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_MIKROBUS_ID));
|
|
\ No newline at end of file
|
|
diff --git a/drivers/misc/mikrobus/mikrobus_manifest.c b/drivers/misc/mikrobus/mikrobus_manifest.c
|
|
new file mode 100644
|
|
index 000000000000..9cacc3d36d4e
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/mikrobus_manifest.c
|
|
@@ -0,0 +1,475 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * mikroBUS manifest parsing, an
|
|
+ * extension to Greybus Manifest Parsing
|
|
+ * under drivers/greybus/manifest.c
|
|
+ *
|
|
+ * Copyright 2014-2015 Google Inc.
|
|
+ * Copyright 2014-2015 Linaro Ltd.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "mikrobus_manifest:%s: " fmt, __func__
|
|
+
|
|
+#include <linux/bits.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/property.h>
|
|
+#include <linux/greybus/greybus_manifest.h>
|
|
+
|
|
+#include "mikrobus_manifest.h"
|
|
+
|
|
+struct manifest_desc {
|
|
+ struct list_head links;
|
|
+ size_t size;
|
|
+ void *data;
|
|
+ enum greybus_descriptor_type type;
|
|
+};
|
|
+
|
|
+static void manifest_descriptor_release_all(struct addon_board_info *board)
|
|
+{
|
|
+ struct manifest_desc *descriptor;
|
|
+ struct manifest_desc *next;
|
|
+
|
|
+ list_for_each_entry_safe(descriptor, next, &board->manifest_descs, links) {
|
|
+ list_del(&descriptor->links);
|
|
+ kfree(descriptor);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int board_descriptor_add(struct addon_board_info *board, struct greybus_descriptor *desc,
|
|
+ size_t size)
|
|
+{
|
|
+ struct greybus_descriptor_header *desc_header = &desc->header;
|
|
+ struct manifest_desc *descriptor;
|
|
+ size_t desc_size;
|
|
+ size_t expected_size;
|
|
+
|
|
+ if (size < sizeof(*desc_header)) {
|
|
+ pr_err("short descriptor (%zu < %zu)", size, sizeof(*desc_header));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ desc_size = le16_to_cpu(desc_header->size);
|
|
+ if (desc_size > size) {
|
|
+ pr_err("incorrect descriptor size (%zu != %zu)", size, desc_size);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ expected_size = sizeof(*desc_header);
|
|
+ switch (desc_header->type) {
|
|
+ case GREYBUS_TYPE_STRING:
|
|
+ expected_size += sizeof(struct greybus_descriptor_string);
|
|
+ expected_size += desc->string.length;
|
|
+ expected_size = ALIGN(expected_size, 4);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_PROPERTY:
|
|
+ expected_size += sizeof(struct greybus_descriptor_property);
|
|
+ expected_size += desc->property.length;
|
|
+ expected_size = ALIGN(expected_size, 4);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_DEVICE:
|
|
+ expected_size += sizeof(struct greybus_descriptor_device);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_MIKROBUS:
|
|
+ expected_size += sizeof(struct greybus_descriptor_mikrobus);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_INTERFACE:
|
|
+ expected_size += sizeof(struct greybus_descriptor_interface);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_CPORT:
|
|
+ expected_size += sizeof(struct greybus_descriptor_cport);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_BUNDLE:
|
|
+ expected_size += sizeof(struct greybus_descriptor_bundle);
|
|
+ break;
|
|
+ case GREYBUS_TYPE_INVALID:
|
|
+ default:
|
|
+ pr_err("invalid descriptor type %d", desc_header->type);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL);
|
|
+ if (!descriptor)
|
|
+ return -ENOMEM;
|
|
+ descriptor->size = desc_size;
|
|
+ descriptor->data = (char *)desc + sizeof(*desc_header);
|
|
+ descriptor->type = desc_header->type;
|
|
+ list_add_tail(&descriptor->links, &board->manifest_descs);
|
|
+ return desc_size;
|
|
+}
|
|
+
|
|
+static char *mikrobus_string_get(struct addon_board_info *board, u8 string_id)
|
|
+{
|
|
+ struct greybus_descriptor_string *desc_string;
|
|
+ struct manifest_desc *descriptor;
|
|
+ bool found = false;
|
|
+ char *string;
|
|
+
|
|
+ if (!string_id)
|
|
+ return NULL;
|
|
+
|
|
+ list_for_each_entry(descriptor, &board->manifest_descs, links) {
|
|
+ if (descriptor->type != GREYBUS_TYPE_STRING)
|
|
+ continue;
|
|
+ desc_string = descriptor->data;
|
|
+ if (desc_string->id == string_id) {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found)
|
|
+ return ERR_PTR(-ENOENT);
|
|
+ string = kmemdup(&desc_string->string, desc_string->length + 1, GFP_KERNEL);
|
|
+ if (!string)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ string[desc_string->length] = '\0';
|
|
+ return string;
|
|
+}
|
|
+
|
|
+static void mikrobus_state_get(struct addon_board_info *board)
|
|
+{
|
|
+ struct greybus_descriptor_mikrobus *mikrobus;
|
|
+ struct greybus_descriptor_interface *interface;
|
|
+ struct manifest_desc *descriptor;
|
|
+ bool found = false;
|
|
+ int i;
|
|
+
|
|
+ list_for_each_entry(descriptor, &board->manifest_descs, links) {
|
|
+ if (descriptor->type == GREYBUS_TYPE_MIKROBUS) {
|
|
+ mikrobus = descriptor->data;
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found) {
|
|
+ pr_err("mikrobus descriptor not found");
|
|
+ return;
|
|
+ }
|
|
+ for (i = 0; i < MIKROBUS_PORT_PIN_COUNT; i++)
|
|
+ board->pin_state[i] = mikrobus->pin_state[i];
|
|
+
|
|
+ found = false;
|
|
+ list_for_each_entry(descriptor, &board->manifest_descs, links) {
|
|
+ if (descriptor->type == GREYBUS_TYPE_INTERFACE) {
|
|
+ interface = descriptor->data;
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found) {
|
|
+ pr_err("interface descriptor not found");
|
|
+ return;
|
|
+ }
|
|
+ board->name = mikrobus_string_get(board, interface->product_stringid);
|
|
+}
|
|
+
|
|
+static struct property_entry *
|
|
+mikrobus_property_entry_get(struct addon_board_info *board, u8 *prop_link,
|
|
+ int num_properties)
|
|
+{
|
|
+ struct greybus_descriptor_property *desc_property;
|
|
+ struct manifest_desc *descriptor;
|
|
+ struct property_entry *properties;
|
|
+ bool found = false;
|
|
+ char *prop_name;
|
|
+ u64 *val_u64;
|
|
+ u32 *val_u32;
|
|
+ u16 *val_u16;
|
|
+ u8 *val_u8;
|
|
+ int i;
|
|
+
|
|
+ properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
|
|
+ if (!properties)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ for (i = 0; i < num_properties; i++) {
|
|
+ list_for_each_entry(descriptor, &board->manifest_descs, links) {
|
|
+ if (descriptor->type != GREYBUS_TYPE_PROPERTY)
|
|
+ continue;
|
|
+ desc_property = descriptor->data;
|
|
+ if (desc_property->id == prop_link[i]) {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found) {
|
|
+ kfree(properties);
|
|
+ return ERR_PTR(-ENOENT);
|
|
+ }
|
|
+ prop_name = mikrobus_string_get(board, desc_property->propname_stringid);
|
|
+ if (!prop_name) {
|
|
+ kfree(properties);
|
|
+ return ERR_PTR(-ENOENT);
|
|
+ }
|
|
+ switch (desc_property->type) {
|
|
+ case MIKROBUS_PROPERTY_TYPE_U8:
|
|
+ val_u8 = kmemdup(&desc_property->value,
|
|
+ (desc_property->length) * sizeof(u8),
|
|
+ GFP_KERNEL);
|
|
+ if (desc_property->length == 1)
|
|
+ properties[i] = PROPERTY_ENTRY_U8(prop_name, *val_u8);
|
|
+ else
|
|
+ properties[i] = PROPERTY_ENTRY_U8_ARRAY_LEN(prop_name,
|
|
+ (void *)desc_property->value, desc_property->length);
|
|
+ break;
|
|
+ case MIKROBUS_PROPERTY_TYPE_U16:
|
|
+ val_u16 = kmemdup(&desc_property->value,
|
|
+ (desc_property->length) * sizeof(u16), GFP_KERNEL);
|
|
+ if (desc_property->length == 1)
|
|
+ properties[i] = PROPERTY_ENTRY_U16(prop_name, *val_u16);
|
|
+ else
|
|
+ properties[i] = PROPERTY_ENTRY_U16_ARRAY_LEN(prop_name,
|
|
+ (void *)desc_property->value, desc_property->length);
|
|
+ break;
|
|
+ case MIKROBUS_PROPERTY_TYPE_U32:
|
|
+ val_u32 = kmemdup(&desc_property->value,
|
|
+ (desc_property->length) * sizeof(u32), GFP_KERNEL);
|
|
+ if (desc_property->length == 1)
|
|
+ properties[i] = PROPERTY_ENTRY_U32(prop_name, *val_u32);
|
|
+ else
|
|
+ properties[i] = PROPERTY_ENTRY_U32_ARRAY_LEN(prop_name,
|
|
+ (void *)desc_property->value, desc_property->length);
|
|
+ break;
|
|
+ case MIKROBUS_PROPERTY_TYPE_U64:
|
|
+ val_u64 = kmemdup(&desc_property->value,
|
|
+ (desc_property->length) * sizeof(u64), GFP_KERNEL);
|
|
+ if (desc_property->length == 1)
|
|
+ properties[i] = PROPERTY_ENTRY_U64(prop_name, *val_u64);
|
|
+ else
|
|
+ properties[i] = PROPERTY_ENTRY_U64_ARRAY_LEN(prop_name,
|
|
+ (void *)desc_property->value, desc_property->length);
|
|
+ break;
|
|
+ default:
|
|
+ kfree(properties);
|
|
+ return ERR_PTR(-EINVAL);
|
|
+ }
|
|
+ }
|
|
+ return properties;
|
|
+}
|
|
+
|
|
+static u8 *mikrobus_property_link_get(struct addon_board_info *board, u8 prop_id,
|
|
+ struct board_device_info *board_dev, u8 prop_type)
|
|
+{
|
|
+ struct greybus_descriptor_property *desc_property;
|
|
+ struct manifest_desc *descriptor;
|
|
+ bool found = false;
|
|
+ u8 *val_u8;
|
|
+
|
|
+ if (!prop_id)
|
|
+ return NULL;
|
|
+ list_for_each_entry(descriptor, &board->manifest_descs, links) {
|
|
+ if (descriptor->type != GREYBUS_TYPE_PROPERTY)
|
|
+ continue;
|
|
+ desc_property = descriptor->data;
|
|
+ if (desc_property->id == prop_id && desc_property->type == prop_type) {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found)
|
|
+ return ERR_PTR(-ENOENT);
|
|
+ val_u8 = kmemdup(&desc_property->value, desc_property->length, GFP_KERNEL);
|
|
+ if (prop_type == MIKROBUS_PROPERTY_TYPE_GPIO)
|
|
+ board_dev->num_gpio_resources = desc_property->length;
|
|
+ else if (prop_type == MIKROBUS_PROPERTY_TYPE_PROPERTY)
|
|
+ board_dev->num_properties = desc_property->length;
|
|
+ else if (prop_type == MIKROBUS_PROPERTY_TYPE_REGULATOR)
|
|
+ board_dev->num_regulators = desc_property->length;
|
|
+ else if (prop_type == MIKROBUS_PROPERTY_TYPE_CLOCK)
|
|
+ board_dev->num_clocks = desc_property->length;
|
|
+ return val_u8;
|
|
+}
|
|
+
|
|
+static int mikrobus_manifest_attach_device(struct addon_board_info *board,
|
|
+ struct greybus_descriptor_device *dev_desc)
|
|
+{
|
|
+ struct board_device_info *board_dev;
|
|
+ struct gpiod_lookup_table *lookup;
|
|
+ struct greybus_descriptor_property *desc_property;
|
|
+ struct manifest_desc *descriptor;
|
|
+ u8 *gpio_desc_link;
|
|
+ u8 *prop_link;
|
|
+ u8 *reg_link;
|
|
+ u8 *clock_link;
|
|
+ u8 *gpioval;
|
|
+ int retval;
|
|
+ int i;
|
|
+
|
|
+ board_dev = kzalloc(sizeof(*board_dev), GFP_KERNEL);
|
|
+ if (!board_dev)
|
|
+ return -ENOMEM;
|
|
+ board_dev->id = dev_desc->id;
|
|
+ board_dev->drv_name = mikrobus_string_get(board, dev_desc->driver_stringid);
|
|
+ if (!board_dev->drv_name) {
|
|
+ retval = -ENOENT;
|
|
+ goto err_free_board_dev;
|
|
+ }
|
|
+ board_dev->protocol = dev_desc->protocol;
|
|
+ board_dev->reg = dev_desc->reg;
|
|
+ board_dev->irq = dev_desc->irq;
|
|
+ board_dev->irq_type = dev_desc->irq_type;
|
|
+ board_dev->max_speed_hz = le32_to_cpu(dev_desc->max_speed_hz);
|
|
+ board_dev->mode = dev_desc->mode;
|
|
+ pr_info("parsed device %d, driver=%s", board_dev->id, board_dev->drv_name);
|
|
+
|
|
+ if (dev_desc->prop_link > 0) {
|
|
+ prop_link = mikrobus_property_link_get(board, dev_desc->prop_link,
|
|
+ board_dev, MIKROBUS_PROPERTY_TYPE_PROPERTY);
|
|
+ if (!prop_link) {
|
|
+ retval = -ENOENT;
|
|
+ goto err_free_board_dev;
|
|
+ }
|
|
+ pr_info("device %d, number of properties=%d", board_dev->id,
|
|
+ board_dev->num_properties);
|
|
+ board_dev->properties = mikrobus_property_entry_get(board, prop_link,
|
|
+ board_dev->num_properties);
|
|
+ }
|
|
+
|
|
+ if (dev_desc->gpio_link > 0) {
|
|
+ gpio_desc_link = mikrobus_property_link_get(board, dev_desc->gpio_link, board_dev,
|
|
+ MIKROBUS_PROPERTY_TYPE_GPIO);
|
|
+ if (!gpio_desc_link) {
|
|
+ retval = -ENOENT;
|
|
+ goto err_free_board_dev;
|
|
+ }
|
|
+ pr_info("device %d, number of gpio resource=%d", board_dev->id,
|
|
+ board_dev->num_gpio_resources);
|
|
+ lookup = kzalloc(struct_size(lookup, table, board_dev->num_gpio_resources),
|
|
+ GFP_KERNEL);
|
|
+ if (!lookup) {
|
|
+ retval = -ENOMEM;
|
|
+ goto err_free_board_dev;
|
|
+ }
|
|
+ for (i = 0; i < board_dev->num_gpio_resources; i++) {
|
|
+ list_for_each_entry(descriptor, &board->manifest_descs, links) {
|
|
+ if (descriptor->type != GREYBUS_TYPE_PROPERTY)
|
|
+ continue;
|
|
+ desc_property = descriptor->data;
|
|
+ if (desc_property->id == gpio_desc_link[i]) {
|
|
+ gpioval = desc_property->value;
|
|
+ lookup->table[i].chip_hwnum = gpioval[0];
|
|
+ lookup->table[i].flags = gpioval[1];
|
|
+ lookup->table[i].con_id =
|
|
+ mikrobus_string_get(board,
|
|
+ desc_property->propname_stringid);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ board_dev->gpio_lookup = lookup;
|
|
+ }
|
|
+
|
|
+ if (dev_desc->reg_link > 0) {
|
|
+ reg_link = mikrobus_property_link_get(board, dev_desc->reg_link,
|
|
+ board_dev, MIKROBUS_PROPERTY_TYPE_REGULATOR);
|
|
+ if (!reg_link) {
|
|
+ retval = -ENOENT;
|
|
+ goto err_free_board_dev;
|
|
+ }
|
|
+ pr_info("device %d, number of regulators=%d", board_dev->id,
|
|
+ board_dev->num_regulators);
|
|
+ board_dev->regulators = mikrobus_property_entry_get(board, reg_link,
|
|
+ board_dev->num_regulators);
|
|
+ }
|
|
+
|
|
+ if (dev_desc->clock_link > 0) {
|
|
+ clock_link = mikrobus_property_link_get(board, dev_desc->clock_link,
|
|
+ board_dev, MIKROBUS_PROPERTY_TYPE_CLOCK);
|
|
+ if (!clock_link) {
|
|
+ retval = -ENOENT;
|
|
+ goto err_free_board_dev;
|
|
+ }
|
|
+ pr_info("device %d, number of clocks=%d", board_dev->id,
|
|
+ board_dev->num_clocks);
|
|
+ board_dev->clocks = mikrobus_property_entry_get(board, clock_link,
|
|
+ board_dev->num_clocks);
|
|
+ }
|
|
+ list_add_tail(&board_dev->links, &board->devices);
|
|
+ return 0;
|
|
+err_free_board_dev:
|
|
+ kfree(board_dev);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static int mikrobus_manifest_parse_devices(struct addon_board_info *board)
|
|
+{
|
|
+ struct greybus_descriptor_device *desc_device;
|
|
+ struct manifest_desc *desc, *next;
|
|
+ int retval;
|
|
+ int devcount = 0;
|
|
+
|
|
+ list_for_each_entry_safe(desc, next, &board->manifest_descs, links) {
|
|
+ if (desc->type != GREYBUS_TYPE_DEVICE)
|
|
+ continue;
|
|
+ desc_device = desc->data;
|
|
+ retval = mikrobus_manifest_attach_device(board, desc_device);
|
|
+ devcount++;
|
|
+ }
|
|
+ return devcount;
|
|
+}
|
|
+
|
|
+int mikrobus_manifest_parse(struct addon_board_info *board, void *data,
|
|
+ size_t size)
|
|
+{
|
|
+ struct greybus_manifest_header *header;
|
|
+ struct greybus_manifest *manifest;
|
|
+ struct greybus_descriptor *desc;
|
|
+ u16 manifest_size;
|
|
+ int dev_count;
|
|
+ int desc_size;
|
|
+
|
|
+ if (size < sizeof(*header)) {
|
|
+ pr_err("short manifest (%zu < %zu)", size, sizeof(*header));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ manifest = data;
|
|
+ header = &manifest->header;
|
|
+ manifest_size = le16_to_cpu(header->size);
|
|
+
|
|
+ if (manifest_size != size) {
|
|
+ pr_err("invalid manifest size(%zu < %u)", size, manifest_size);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (header->version_major > MIKROBUS_VERSION_MAJOR) {
|
|
+ pr_err("manifest version too new (%u.%u > %u.%u)",
|
|
+ header->version_major, header->version_minor,
|
|
+ MIKROBUS_VERSION_MAJOR, MIKROBUS_VERSION_MINOR);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ desc = manifest->descriptors;
|
|
+ size -= sizeof(*header);
|
|
+ while (size) {
|
|
+ desc_size = board_descriptor_add(board, desc, size);
|
|
+ if (desc_size < 0) {
|
|
+ pr_err("invalid manifest descriptor, size: %u", desc_size);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ desc = (void *)desc + desc_size;
|
|
+ size -= desc_size;
|
|
+ }
|
|
+ mikrobus_state_get(board);
|
|
+ dev_count = mikrobus_manifest_parse_devices(board);
|
|
+ pr_info(" %s manifest parsed with %d devices", board->name, dev_count);
|
|
+ manifest_descriptor_release_all(board);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+size_t mikrobus_manifest_header_validate(void *data, size_t size)
|
|
+{
|
|
+ struct greybus_manifest_header *header;
|
|
+ u16 manifest_size;
|
|
+
|
|
+ if (size < sizeof(*header)) {
|
|
+ pr_err("short manifest (%zu < %zu)", size, sizeof(*header));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ header = data;
|
|
+ manifest_size = le16_to_cpu(header->size);
|
|
+ if (header->version_major > MIKROBUS_VERSION_MAJOR) {
|
|
+ pr_err("manifest version too new (%u.%u > %u.%u)",
|
|
+ header->version_major, header->version_minor,
|
|
+ MIKROBUS_VERSION_MAJOR, MIKROBUS_VERSION_MINOR);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return manifest_size;
|
|
+}
|
|
+
|
|
diff --git a/drivers/misc/mikrobus/mikrobus_manifest.h b/drivers/misc/mikrobus/mikrobus_manifest.h
|
|
new file mode 100644
|
|
index 000000000000..36b64b2093f5
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mikrobus/mikrobus_manifest.h
|
|
@@ -0,0 +1,20 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/*
|
|
+ * mikroBUS manifest definition
|
|
+ * extension to Greybus Manifest Definition
|
|
+ *
|
|
+ * Copyright 2014-2015 Google Inc.
|
|
+ * Copyright 2014-2015 Linaro Ltd.
|
|
+ *
|
|
+ * Released under the GPLv2 and BSD licenses.
|
|
+ */
|
|
+
|
|
+#ifndef __MIKROBUS_MANIFEST_H
|
|
+#define __MIKROBUS_MANIFEST_H
|
|
+
|
|
+#include "mikrobus_core.h"
|
|
+
|
|
+int mikrobus_manifest_parse(struct addon_board_info *info, void *data, size_t size);
|
|
+size_t mikrobus_manifest_header_validate(void *data, size_t size);
|
|
+
|
|
+#endif /* __MIKROBUS_MANIFEST_H */
|
|
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c
|
|
index b3e1792d9c49..5b16717c8415 100644
|
|
--- a/drivers/w1/w1_int.c
|
|
+++ b/drivers/w1/w1_int.c
|
|
@@ -243,3 +243,31 @@ void w1_remove_master_device(struct w1_bus_master *bm)
|
|
__w1_remove_master_device(found);
|
|
}
|
|
EXPORT_SYMBOL(w1_remove_master_device);
|
|
+
|
|
+/**
|
|
+ * w1_find_master_device() - find a master device
|
|
+ * @bm: master bus device to search
|
|
+ */
|
|
+struct w1_master *w1_find_master_device(struct w1_bus_master *bm)
|
|
+{
|
|
+ struct w1_master *dev, *found = NULL;
|
|
+
|
|
+ list_for_each_entry(dev, &w1_masters, w1_master_entry) {
|
|
+ if (!dev->initialized)
|
|
+ continue;
|
|
+
|
|
+ if (dev->bus_master->data == bm->data) {
|
|
+ found = dev;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!found) {
|
|
+ pr_err("device doesn't exist.\n");
|
|
+ return ERR_PTR(-ENODEV);
|
|
+ }
|
|
+
|
|
+ return found;
|
|
+}
|
|
+EXPORT_SYMBOL(w1_find_master_device);
|
|
+
|
|
diff --git a/include/linux/greybus/greybus_manifest.h b/include/linux/greybus/greybus_manifest.h
|
|
index 6e62fe478712..50d5a5fd3d51 100644
|
|
--- a/include/linux/greybus/greybus_manifest.h
|
|
+++ b/include/linux/greybus/greybus_manifest.h
|
|
@@ -23,6 +23,9 @@ enum greybus_descriptor_type {
|
|
GREYBUS_TYPE_STRING = 0x02,
|
|
GREYBUS_TYPE_BUNDLE = 0x03,
|
|
GREYBUS_TYPE_CPORT = 0x04,
|
|
+ GREYBUS_TYPE_MIKROBUS = 0x05,
|
|
+ GREYBUS_TYPE_PROPERTY = 0x06,
|
|
+ GREYBUS_TYPE_DEVICE = 0x07,
|
|
};
|
|
|
|
enum greybus_protocol {
|
|
@@ -151,6 +154,49 @@ struct greybus_descriptor_cport {
|
|
__u8 protocol_id; /* enum greybus_protocol */
|
|
} __packed;
|
|
|
|
+/*
|
|
+ * A mikrobus descriptor is used to describe the details
|
|
+ * about the bus ocnfiguration for the add-on board
|
|
+ * connected to the mikrobus port.
|
|
+ */
|
|
+struct greybus_descriptor_mikrobus {
|
|
+ __u8 pin_state[12];
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * A property descriptor is used to pass named properties
|
|
+ * to device drivers through the unified device properties
|
|
+ * interface under linux/property.h
|
|
+ */
|
|
+struct greybus_descriptor_property {
|
|
+ __u8 length;
|
|
+ __u8 id;
|
|
+ __u8 propname_stringid;
|
|
+ __u8 type;
|
|
+ __u8 value[0];
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * A device descriptor is used to describe the
|
|
+ * details required by a add-on board device
|
|
+ * driver.
|
|
+ */
|
|
+struct greybus_descriptor_device {
|
|
+ __u8 id;
|
|
+ __u8 driver_stringid;
|
|
+ __u8 protocol;
|
|
+ __u8 reg;
|
|
+ __le32 max_speed_hz;
|
|
+ __u8 irq;
|
|
+ __u8 irq_type;
|
|
+ __u8 mode;
|
|
+ __u8 prop_link;
|
|
+ __u8 gpio_link;
|
|
+ __u8 reg_link;
|
|
+ __u8 clock_link;
|
|
+ __u8 pad[1];
|
|
+} __packed;
|
|
+
|
|
struct greybus_descriptor_header {
|
|
__le16 size;
|
|
__u8 type; /* enum greybus_descriptor_type */
|
|
@@ -164,6 +210,9 @@ struct greybus_descriptor {
|
|
struct greybus_descriptor_interface interface;
|
|
struct greybus_descriptor_bundle bundle;
|
|
struct greybus_descriptor_cport cport;
|
|
+ struct greybus_descriptor_mikrobus mikrobus;
|
|
+ struct greybus_descriptor_property property;
|
|
+ struct greybus_descriptor_device device;
|
|
};
|
|
} __packed;
|
|
|
|
diff --git a/include/linux/w1.h b/include/linux/w1.h
|
|
index cebf3464bc03..2076f9a289cf 100644
|
|
--- a/include/linux/w1.h
|
|
+++ b/include/linux/w1.h
|
|
@@ -242,6 +242,7 @@ struct w1_master {
|
|
|
|
int w1_add_master_device(struct w1_bus_master *master);
|
|
void w1_remove_master_device(struct w1_bus_master *master);
|
|
+struct w1_master *w1_find_master_device(struct w1_bus_master *master);
|
|
|
|
/**
|
|
* struct w1_family_ops - operations for a family type
|
|
--
|
|
2.25.1
|
|
|