This is implemented as a platform device and instantiated using DT. Signed-off-by: Jan Luebbe <jluebbe@debian.org>master
parent
006dbf34cd
commit
09476e9d9b
@ -0,0 +1,208 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "sob_odu_eeprom.h"
|
||||
|
||||
static ssize_t board_version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct sob_odu_eeprom *soe = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", soe->model);
|
||||
}
|
||||
|
||||
static ssize_t serial_number_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct sob_odu_eeprom *soe = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", soe->serial);
|
||||
}
|
||||
|
||||
static ssize_t hw_options_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct sob_odu_eeprom *soe = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", soe->hw_options);
|
||||
}
|
||||
|
||||
static ssize_t mfg_date_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct sob_odu_eeprom *soe = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", soe->manuf_date);
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(board_version, 0444, board_version_show, NULL);
|
||||
static const DEVICE_ATTR(serial_number, 0444, serial_number_show, NULL);
|
||||
static const DEVICE_ATTR(hw_options, 0444, hw_options_show, NULL);
|
||||
static const DEVICE_ATTR(mfg_date, 0444, mfg_date_show, NULL);
|
||||
|
||||
static const struct attribute *sobodu_attrs[] = {
|
||||
&dev_attr_board_version.attr,
|
||||
&dev_attr_serial_number.attr,
|
||||
&dev_attr_hw_options.attr,
|
||||
&dev_attr_mfg_date.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group sobodu_attr_group = {
|
||||
.attrs = (struct attribute **) sobodu_attrs,
|
||||
};
|
||||
|
||||
#define G2I(bank, io) ((bank)*32 + (io))
|
||||
|
||||
static const struct gpio gpio_exp[] = {
|
||||
{ G2I(1,16), GPIOF_OUT_INIT_HIGH, "!ais_off" },
|
||||
{ G2I(1,20), GPIOF_IN, "ais_rx_ind" },
|
||||
{ G2I(1,24), GPIOF_IN, "ais_status_ind" },
|
||||
{ G2I(0,27), GPIOF_IN, "ais_error_ind" },
|
||||
{ G2I(0,23), GPIOF_IN, "ais_powerok_ind" },
|
||||
|
||||
{ G2I(0,22), GPIOF_IN, "!mdm1_pwr_ind" },
|
||||
{ G2I(0,26), GPIOF_OUT_INIT_LOW, "mdm1_rst" },
|
||||
{ G2I(1,27), GPIOF_OUT_INIT_HIGH, "mdm1_on" },
|
||||
{ G2I(1,26), GPIOF_OUT_INIT_HIGH, "mdm_ldo_en" },
|
||||
{ G2I(1,25), GPIOF_IN, "!mdm2_pwr_ind" },
|
||||
{ G2I(1,23), GPIOF_OUT_INIT_LOW, "mdm2_rst" },
|
||||
{ G2I(1,18), GPIOF_OUT_INIT_HIGH, "mdm2_on" },
|
||||
|
||||
/* G2I(1,19) is now registered by the at24 driver */
|
||||
|
||||
{ G2I(3,17), GPIOF_IN, "gnss_fix" },
|
||||
/* G2I(3,19) is now registered to the PPS core */
|
||||
{ G2I(3,18), GPIOF_OUT_INIT_HIGH, "!gnss_rst" },
|
||||
|
||||
{ G2I(1,22), GPIOF_IN, "adsb_frame_ind" },
|
||||
{ G2I(1,21), GPIOF_OUT_INIT_HIGH, "!adsb_rst" },
|
||||
|
||||
{ G2I(3,8), GPIOF_IN, "acc_int1" },
|
||||
{ G2I(3,7), GPIOF_IN, "acc_int2" },
|
||||
|
||||
{ G2I(1,30), GPIOF_OUT_INIT_HIGH, "!hub_reset" },
|
||||
};
|
||||
|
||||
static int sob_odu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sob_odu_eeprom *soe;
|
||||
struct nvmem_device *nvmem;
|
||||
uint32_t crc32_comp;
|
||||
int i, rc;
|
||||
|
||||
soe = devm_kzalloc(&pdev->dev, sizeof(struct sob_odu_eeprom),
|
||||
GFP_KERNEL);
|
||||
if (!soe)
|
||||
return -ENOMEM;
|
||||
|
||||
nvmem = devm_nvmem_device_get(&pdev->dev, "baseboard");
|
||||
if (IS_ERR(nvmem)) {
|
||||
if (PTR_ERR(nvmem) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "failed to get nvmem\n");
|
||||
return PTR_ERR(nvmem);
|
||||
}
|
||||
|
||||
rc = nvmem_device_read(nvmem, 0, sizeof(*soe), soe);
|
||||
if (rc < sizeof(*soe)) {
|
||||
dev_err(&pdev->dev, "failed to read nvmem\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (soe->magic != SOBJB_EE_MAGIC || soe->version != SOBJB_EE_VERSION) {
|
||||
dev_err(&pdev->dev, "EEPROM magic/version wrong\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
crc32_comp = crc32_le(0xffffffff, (void *)soe + 8, sizeof(*soe)-8);
|
||||
if (soe->crc32 != crc32_comp) {
|
||||
dev_err(&pdev->dev, "EEPROM CRC32 mismatch (eeprom=0x%08x, computed=0x%08x)\n",
|
||||
soe->crc32, crc32_comp);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_info(&pdev->dev, "SOB-ODU board v%u, serial number %u, mfg %u, hw_options=0x%08x\n",
|
||||
soe->model, soe->serial, soe->manuf_date, soe->hw_options);
|
||||
|
||||
/* FIXME: export the board version via sysfs */
|
||||
/* FIXME: configure the MAC addresses of the wlan adapters */
|
||||
|
||||
/* export all of the GPIOs from the above array */
|
||||
rc = gpio_request_array(gpio_exp, ARRAY_SIZE(gpio_exp));
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "failed to request GPIOs\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gpio_exp); i++) {
|
||||
bool chg_dir_perm = false;
|
||||
char label[64];
|
||||
|
||||
/* this is the only one where the direction may be
|
||||
* changed */
|
||||
if (gpio_exp[i].gpio == G2I(3,7))
|
||||
chg_dir_perm = true;
|
||||
|
||||
/* export it to sysfs */
|
||||
rc = gpio_export(gpio_exp[i].gpio, chg_dir_perm);
|
||||
if (rc)
|
||||
dev_warn(&pdev->dev, "unable to export GPIO %u\n", gpio_exp[i].gpio);
|
||||
|
||||
/* set active-low attribute to invert logic */
|
||||
if (gpio_exp[i].label[0] == '!') {
|
||||
gpio_sysfs_set_active_low(gpio_exp[i].gpio, true);
|
||||
/* skip initial '!' of negated signals */
|
||||
snprintf(label, sizeof(label)-1, "gpio_%s", gpio_exp[i].label + 1);
|
||||
} else
|
||||
snprintf(label, sizeof(label)-1, "gpio_%s", gpio_exp[i].label);
|
||||
|
||||
/* generate symlink to /sys/devices/platform/sob-odu.0 */
|
||||
rc = gpio_export_link(&pdev->dev, label,
|
||||
gpio_exp[i].gpio);
|
||||
if (rc)
|
||||
dev_warn(&pdev->dev, "unable to symlink GPIO %u\n", gpio_exp[i].gpio);
|
||||
}
|
||||
|
||||
switch (soe->model) {
|
||||
case SOB_ODU_V3: {
|
||||
int gpio37 = G2I(3,7);
|
||||
/* GPIO_3_7 is AIS VBUS enable, not ACC_INT2 */
|
||||
sysfs_remove_link(&pdev->dev.kobj, "acc_int2");
|
||||
gpio_unexport(gpio37);
|
||||
gpio_free(gpio37);
|
||||
gpio_request(gpio37, "ais_vbus_enable");
|
||||
gpio_direction_output(gpio37, true);
|
||||
gpio_export(gpio37, false);
|
||||
gpio_sysfs_set_active_low(gpio37, false);
|
||||
gpio_export_link(&pdev->dev, "gpio_ais_vbus_enable",
|
||||
gpio37);
|
||||
break;}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, soe);
|
||||
if (sysfs_create_group(&pdev->dev.kobj, &sobodu_attr_group))
|
||||
dev_err(&pdev->dev, "failed to create sysfs group\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sob_odu_dt_ids[] = {
|
||||
{ .compatible = "sysmocom,sob-odu-platform", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sob_odu_dt_ids);
|
||||
|
||||
static struct platform_driver sob_odu_driver = {
|
||||
.probe = sob_odu_probe,
|
||||
.driver = {
|
||||
.name = "sob-odu-platform",
|
||||
.of_match_table = sob_odu_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sob_odu_driver);
|
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#define MAC_ADDR_LEN 6
|
||||
|
||||
#define SOBJB_EE_MAGIC 0x50b0d0ee
|
||||
#define SOBJB_EE_VERSION 1
|
||||
|
||||
#define SOB_ODU_V1 1
|
||||
#define SOB_ODU_V2 2
|
||||
#define SOB_ODU_V3 3
|
||||
|
||||
struct sob_odu_eeprom {
|
||||
uint32_t magic; /* SOBJB_EE_MAGIC */
|
||||
uint32_t crc32; /* crc32_le */
|
||||
|
||||
uint16_t len; /* sizeof(struct sob_odu_eeprom) */
|
||||
uint8_t version; /* SOBJB_EE_VERSION */
|
||||
uint8_t model; /* SOB_ODU_V* */
|
||||
|
||||
uint32_t serial;
|
||||
uint32_t manuf_date;
|
||||
uint32_t hw_options;
|
||||
|
||||
uint8_t mac_eth0[MAC_ADDR_LEN];
|
||||
uint8_t mac_eth1[MAC_ADDR_LEN];
|
||||
uint8_t mac_wlan0[MAC_ADDR_LEN];
|
||||
uint8_t mac_wlan1[MAC_ADDR_LEN];
|
||||
} __attribute__ ((packed));
|
Loading…
Reference in new issue