BigW Consortium Gitlab

Commit 33b7040e by David Frey

Introduce IoT slot kernel driver

The IoT slot driver provides a more modular replacement for the mangoh driver which is part of the legato framework. The idea is to separate out the installation of various sensor devices and the IoT card framework.
parent a1d14f8f
sources:
{
mangoh_iot.c
green.c
eeprom.c
iot-slot-core.c
iot-slot-eeprom.c
}
params:
{
model = "green"
}
/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* MangOH green platform has a pca9548 I2C switch with three sx150 GPIO
* expanders. GPIOs 11, 13 and 12 on the second GPIO expander are card
* detect signals for slots 1, 2 and 3, respectively. We need to probe
* those GPIO signals to determine which cards are present. GPIOs
* 10-15 on the first expander, as well as GPIO 8 on the third expander
* control the SDIO, SPI and UART switches on the board.
*
* Once an IoT card is detected, we read the at24 EEPROM on the card
* to determine type of card and add the appropriate device type. This
* should also insmod/launch an appropriate card driver. Some bus types
* (e.g. USB or SDIO) have the auto-detect capability so the device
* gets added once the board is taken out of reset and its switch
* configured.
*
* Assuming that the first GPIO line on the card is always used for
* interrupts, we can hard-code each card's IRQ and leave it to the
* driver to request it.
*/
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/i2c/pca954x.h>
#include <linux/i2c/sx150x.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/spi/spi.h>
#include "mangoh.h"
#include "eeprom.h"
static struct sx150x_platform_data sx150x_data[] = {
[0] = {
.gpio_base = -1,
.oscio_is_gpo = false,
.io_pullup_ena = 0,
.io_pulldn_ena = 0,
.io_open_drain_ena = 0,
.io_polarity = 0,
.irq_summary = -1,
.irq_base = -1,
},
[1] = {
.gpio_base = -1,
.oscio_is_gpo = false,
.io_pullup_ena = 0x3800, /* pulled low when card present */
.io_pulldn_ena = 0,
.io_open_drain_ena = 0,
.io_polarity = 0,
.irq_summary = -1,
.irq_base = -1,
},
[2] = {
.gpio_base = -1,
.oscio_is_gpo = false,
.io_pullup_ena = 0,
.io_pulldn_ena = 0,
.io_open_drain_ena = 0,
.io_polarity = 0,
.irq_summary = -1,
.irq_base = -1,
},
};
static struct i2c_board_info sx150x_devinfo[] = {
[0] = {
I2C_BOARD_INFO("sx1509q", 0x3e),
.platform_data = &(sx150x_data[0]),
.irq = 0,
},
[1] = {
I2C_BOARD_INFO("sx1509q", 0x3f),
.platform_data = &(sx150x_data[1]),
.irq = 0,
},
[2] = {
I2C_BOARD_INFO("sx1509q", 0x70),
.platform_data = &(sx150x_data[2]),
.irq = 0,
},
};
static struct pca954x_platform_mode pca954x_adap_modes[] = {
{1, 1, 0}, {2, 1, 0}, {3, 1, 0}, {4, 1, 0},
{5, 1, 0}, {6, 1, 0}, {7, 1, 0}, {8, 1, 0}
};
static struct pca954x_platform_data pca954x_pdata = {
pca954x_adap_modes,
ARRAY_SIZE(pca954x_adap_modes),
};
static struct i2c_board_info pca954x_device_info = {
I2C_BOARD_INFO("pca9548", 0x71),
.platform_data = &pca954x_pdata,
.irq = 0,
};
static struct green_platform_data {
struct i2c_client *mux;
struct i2c_client *expander[ARRAY_SIZE(sx150x_data)];
struct slot_status {
/*
* Assigned GPIOs on IoT connector. Index 0-3 cover the
* dedicated GPIO pins of the IoT card interface. Index 5 and 6
* are for the card detect and card reset functions.
*/
int gpio[6];
bool present; /* card present */
void *busdev[mangoh_bus_last]; /* IoT bus devices */
} slot[3];
int sdio_sel_gpio;
int spi_sw_en_gpio;
int spi_sw_gpio;
int uart0_sw_en_gpio;
int uart0_sw_gpio;
int uart1_sw_en_gpio;
int uart1_sw_gpio;
} green_pdata = {
NULL, /* Mux */
{ /* Expanders */
NULL
},
{ /*
* Slots: GPIO #s are relative to GPIO base on its chip
* GPIOs present busdev
*/
{{80, 78, 84, 29, 11, 4}, false, {NULL}},
{{73, 79, 30, 50, 13, 3}, false, {NULL}},
{{49, 54, 61, 92, 12, 2}, false, {NULL}},
},
13, 14, 15, 10, 11, 8, 12,
};
#define det_gpio gpio[4] /* slot detect */
#define rst_gpio gpio[5] /* slot reset */
#define i2cdev busdev[mangoh_bus_i2c]
#define spidev busdev[mangoh_bus_spi]
#define uartdev busdev[mangoh_bus_uart]
#define sdiodev busdev[mangoh_bus_sdio]
#define usbdev busdev[mangoh_bus_usb]
#define gpiodev busdev[mangoh_bus_gpio]
#define pcmdev busdev[mangoh_bus_pcm]
#define adcdev busdev[mangoh_bus_adc]
/* Helper functions for accessing MangOH green slots */
static inline int green_num_slots(struct platform_device *pdev)
{
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
return ARRAY_SIZE(pdata->slot);
}
static inline struct slot_status *green_get_slot(struct platform_device *pdev,
int slot)
{
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
return &(pdata->slot[slot]);
}
static struct device *green_add_gpio(struct platform_device *pdev,
int slot,
struct list_head *item)
{
int i;
struct slot_status *s = green_get_slot(pdev, slot);
for (i = 0; i < 4; i++) {
uint8_t cfg = eeprom_if_gpio_cfg(item, i);
switch (cfg)
{
case EEPROM_GPIO_CFG_OUTPUT_LOW:
gpio_direction_output(s->gpio[i], 0);
break;
case EEPROM_GPIO_CFG_OUTPUT_HIGH:
gpio_direction_output(s->gpio[i], 1);
break;
case EEPROM_GPIO_CFG_INPUT_PULL_UP:
gpio_direction_input(s->gpio[i]);
gpio_pull_up(s->gpio[i]);
break;
case EEPROM_GPIO_CFG_INPUT_PULL_DOWN:
gpio_direction_input(s->gpio[i]);
gpio_pull_down(s->gpio[i]);
break;
case EEPROM_GPIO_CFG_INPUT_FLOATING:
gpio_direction_input(s->gpio[i]);
break;
default:
/* reserved, ignore */
break;
}
}
return &pdev->dev; /* return anything non-null */
}
/* Add I2C device detected in slot */
static struct i2c_client *green_add_i2c(struct platform_device *pdev,
int slot,
struct list_head *item)
{
struct i2c_adapter *adapter;
struct i2c_client *i2cdev;
struct i2c_board_info board = {};
struct slot_status *s = green_get_slot(pdev, slot);
uint8_t irq_gpio;
strncpy(board.type, eeprom_if_i2c_modalias(item), sizeof(board.type));
irq_gpio = eeprom_if_i2c_irq_gpio(item);
if (irq_gpio != IRQ_GPIO_UNUSED)
{
board.irq = gpio_to_irq(s->gpio[irq_gpio]);
}
board.addr = eeprom_if_i2c_address(item);
/* TODO: Find intelligent way to assign platform data */
/* Lookup I2C adapter */
adapter = i2c_get_adapter(1 + slot);
if (!adapter) {
dev_err(&pdev->dev, "No I2C adapter for slot %d.\n", slot);
return NULL;
}
i2cdev = i2c_new_device(adapter, &board);
i2c_put_adapter(adapter);
return i2cdev;
}
/* Remove I2C device from slot */
static void green_remove_i2c(struct platform_device *pdev, int slot)
{
struct slot_status *s = green_get_slot(pdev, slot);
i2c_unregister_device(s->i2cdev);
s->i2cdev = NULL;
}
/* Add SPI device detected in slot */
static struct spi_device *green_add_spi(struct platform_device *pdev,
int slot,
struct list_head *item)
{
struct slot_status *s;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct spi_board_info board = {
.max_speed_hz = 2*1000*1000,
.mode = SPI_MODE_0,
.platform_data = NULL,
.bus_num = 0,
.chip_select = 0,
};
struct spi_master *master;
uint8_t irq_gpio;
s = green_get_slot(pdev, slot);
/* Assign IRQ number */
irq_gpio = eeprom_if_spi_irq_gpio(item);
if (irq_gpio != IRQ_GPIO_UNUSED)
{
board.irq = gpio_to_irq(s->gpio[irq_gpio]);
}
strncpy(board.modalias, eeprom_if_spi_modalias(item),
sizeof(board.modalias));
master = spi_busnum_to_master(0);
if (!master) {
dev_err(&pdev->dev, "No master for SPI bus 0.\n");
return NULL;
}
/* Switch SPI bus to selected slot */
gpio_set_value_cansleep(pdata->spi_sw_gpio, 1 - slot);
return spi_new_device(master, &board);
}
/* Remove SPI device from slot */
static void green_remove_spi(struct platform_device *pdev, int slot)
{
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct slot_status *s = green_get_slot(pdev, slot);
spi_unregister_device(s->spidev);
s->spidev = NULL;
/* Restore SPI switch default value */
gpio_set_value_cansleep(pdata->spi_sw_gpio, 0);
}
/* Assign UART device to slot */
static struct device *green_add_uart(struct platform_device *pdev,
int slot,
struct list_head *item)
{
struct device *ttydev;
char *ttyname;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (slot < 2) {
ttyname = "msm_serial_hs.0";
gpio_set_value_cansleep(pdata->uart0_sw_gpio, 1 - slot);
} else {
ttyname = "msm_serial_hsl.1";
gpio_set_value_cansleep(pdata->uart1_sw_gpio, 3 - slot);
}
ttydev = bus_find_device_by_name(&platform_bus_type, NULL, ttyname);
if (!ttydev) {
dev_err(&pdev->dev, "Slot%d: No UART %s\n", slot, ttyname);
return NULL;
}
dev_dbg(&pdev->dev, "Slot%d: Using UART %s.\n", slot, ttyname);
return ttydev;
}
/* De-assign UART device from slot */
static void green_remove_uart(struct platform_device *pdev, int slot)
{
int sw_gpio;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct slot_status *s = green_get_slot(pdev, slot);
/* Restore switch GPIO to original value */
sw_gpio = (slot < 2 ? pdata->uart0_sw_gpio : pdata->uart1_sw_gpio);
gpio_set_value_cansleep(sw_gpio, 0);
s->uartdev = NULL;
}
/* Enable SDIO bus in slot 1. Note: only slot 1 supports SDIO */
static struct device *green_add_sdio(struct platform_device *pdev,
int slot,
struct list_head *item)
{
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (0 != slot) {
dev_err(&pdev->dev, "Slot%d: SDIO unsupported.\n", slot);
return NULL;
}
gpio_set_value_cansleep(pdata->sdio_sel_gpio, 0);
return &pdev->dev; /* just return anything non-NULL */
}
/* Disable SDIO bus in slot 1. */
static void green_remove_sdio(struct platform_device *pdev, int slot)
{
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct slot_status *s = green_get_slot(pdev, slot);
if (0 != slot) {
dev_err(&pdev->dev, "Slot%d: SDIO unsupported.\n", slot);
return;
}
gpio_set_value_cansleep(pdata->sdio_sel_gpio, 1);
s->sdiodev = NULL;
}
/* Read slot EEPROM and create appropriate device(s). */
static int scan_slot_eeprom(struct platform_device *pdev, int slot)
{
int rv;
struct list_head *item;
struct slot_status *s;
struct i2c_client *eeprom;
/* Pull slot-detect GPIO high and load EEPROM */
s = green_get_slot(pdev, slot);
gpio_direction_output(s->det_gpio, 1);
eeprom = eeprom_load(slot);
if (NULL == eeprom) {
dev_err(&pdev->dev, "Slot%d: No EEPROM.\n", slot);
rv = -ENODEV;
goto eeprom_detect_failed;
}
dev_dbg(&pdev->dev, "Slot%d: Found EEPROM\n", slot);
/* Take IoT board out of reset */
gpio_set_value_cansleep(s->rst_gpio, 1);
list_for_each(item, eeprom_if_list(eeprom)) {
if (eeprom_is_if_gpio(item)) {
dev_info(&eeprom->dev, "\t\tGPIO device\n");
s->gpiodev = green_add_gpio(pdev, slot, item);
}
if (eeprom_is_if_plat(item)) {
dev_info(&eeprom->dev, "\t\tPlatform device\n");
}
if (eeprom_is_if_i2c(item)) {
dev_info(&eeprom->dev, "\t\tI2C device\n");
s->i2cdev = green_add_i2c(pdev, slot, item);
}
if (eeprom_is_if_spi(item)) {
dev_info(&eeprom->dev, "\t\tSPI device\n");
s->spidev = green_add_spi(pdev, slot, item);
}
if (eeprom_is_if_usb(item))
dev_info(&eeprom->dev, "\t\tUSB device\n");
if (eeprom_is_if_sdio(item)) {
dev_info(&eeprom->dev, "\t\tSDIO device\n");
s->sdiodev = green_add_sdio(pdev, slot, item);
}
if (eeprom_is_if_adc(item))
dev_info(&eeprom->dev, "\t\tADC device\n");
if (eeprom_is_if_pcm(item))
dev_info(&eeprom->dev, "\t\tPCM device\n");
if (eeprom_is_if_uart(item)) {
dev_info(&eeprom->dev, "\t\tUART device\n");
s->uartdev = green_add_uart(pdev, slot, item);
}
}
rv = eeprom_num_slots(eeprom);
eeprom_detect_failed:
/* Free EEPROM and restore GPIO direction */
eeprom_unload(eeprom);
gpio_direction_input(s->det_gpio);
return rv;
}
#define SETUP_GPIO(dev, gpio, name) do { \
int r = devm_gpio_request((dev), (gpio), (name)); \
if (r) { \
dev_err((dev), "%s (GPIO%d): error %d\n", \
(name), (gpio), -r); \
return r; \
} \
} while (0)
#define SETUP_INPUT_GPIO(dev, gpio, name) do { \
SETUP_GPIO((dev), (gpio), (name)); \
gpio_direction_input((gpio)); \
} while (0)
#define SETUP_OUTPUT_GPIO(dev, gpio, name, value) do { \
SETUP_GPIO((dev), (gpio), (name)); \
gpio_direction_output((gpio), (value)); \
} while (0)
/* Probe slots for device presence. Configure devices if present */
static int green_probe_slots(struct platform_device *pdev)
{
int i, j, rv;
struct slot_status *s;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct gpio_chip *en_chip = i2c_get_clientdata(pdata->expander[0]);
struct gpio_chip *det_chip = i2c_get_clientdata(pdata->expander[1]);
struct gpio_chip *rst_chip = i2c_get_clientdata(pdata->expander[2]);
/* Adjust GPIO numbers for SPI and UART switches and acquire GPIOs */
pdata->sdio_sel_gpio += en_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->sdio_sel_gpio, "SDIO_sel", 1);
pdata->spi_sw_en_gpio += en_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->spi_sw_en_gpio, "SPI_sw_en", 0);
pdata->spi_sw_gpio += en_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->spi_sw_gpio, "SPI_en", 0);
pdata->uart0_sw_en_gpio += en_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->uart0_sw_en_gpio, "UART0_sw_en", 0);
pdata->uart0_sw_gpio += en_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->uart0_sw_gpio, "UART0_sw", 0);
pdata->uart1_sw_en_gpio += rst_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->uart1_sw_en_gpio, "UART1_sw_en", 0);
pdata->uart1_sw_gpio += en_chip->base;
SETUP_OUTPUT_GPIO(&pdev->dev, pdata->uart1_sw_gpio, "UART1_sw", 0);
for (i = 0; i < green_num_slots(pdev); i++) {
char name[32];
s = green_get_slot(pdev, i);
/* Let's first adjust the gpio number to be global */
s->det_gpio += det_chip->base;
s->rst_gpio += rst_chip->base;
/* IRQ GPIOs not routed through expanders */
/* Now request the "slot-present" GPIO, input direction */
snprintf(name, sizeof(name), "Slot%d_detect", i);
SETUP_INPUT_GPIO(&pdev->dev, s->det_gpio, name);
/* "present" is active low */
s->present = !gpio_get_value_cansleep(s->det_gpio);
dev_info(&pdev->dev, "%s (GPIO%d): %s\n", name,
s->det_gpio, (s->present ? "present" : "absent"));
/* Take the slot reset GPIO */
snprintf(name, sizeof(name), "Slot%d_reset", i);
SETUP_OUTPUT_GPIO(&pdev->dev, s->rst_gpio, name, 0);
/* Take the slot GPIOs */
for (j = 0; j < 4; j++) {
snprintf(name, sizeof(name), "Slot%d_GPIO%d", i, j);
SETUP_INPUT_GPIO(&pdev->dev, s->gpio[j], name);
}
}
/* Map devices in slots */
i = 0;
while(i < green_num_slots(pdev)) {
rv = 1;
s = green_get_slot(pdev, i);
if (s->present)
rv = scan_slot_eeprom(pdev, i);
/* Ignore slots where eeprom could not be parsed */
if (rv < 1) {
s->present = false;
i++; /* skip the slot with the unreadable eeprom */
}
else {
i += rv; /* rv contains number of occupied slots */
}
}
return 0;
}
/* Release all devices from slots and free GPIOs. */
static void green_release_slots(struct platform_device *pdev)
{
int i, j;
struct slot_status *s;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
/* Release GPIOs */
for (i = 0; i < green_num_slots(pdev); i++) {
s = green_get_slot(pdev, i);
/* TODO: check all busdev types */
if (s->i2cdev)
green_remove_i2c(pdev, i);
if (s->spidev)
green_remove_spi(pdev, i);
if (s->uartdev)
green_remove_uart(pdev, i);
if (s->sdiodev)
green_remove_sdio(pdev, i);
gpio_direction_input(s->det_gpio);
gpio_direction_input(s->rst_gpio);
devm_gpio_free(&pdev->dev, s->rst_gpio);
devm_gpio_free(&pdev->dev, s->det_gpio);
for (j = 0; j < 4; j++)
devm_gpio_free(&pdev->dev, s->gpio[j]);
}
/* Release switch control GPIOs */
devm_gpio_free(&pdev->dev, pdata->sdio_sel_gpio);
devm_gpio_free(&pdev->dev, pdata->spi_sw_en_gpio);
devm_gpio_free(&pdev->dev, pdata->spi_sw_gpio);
devm_gpio_free(&pdev->dev, pdata->uart0_sw_en_gpio);
devm_gpio_free(&pdev->dev, pdata->uart0_sw_gpio);
devm_gpio_free(&pdev->dev, pdata->uart1_sw_en_gpio);
devm_gpio_free(&pdev->dev, pdata->uart1_sw_gpio);
}
/* Map the MangOH green IoT slots. */
static int mangoh_green_map(struct platform_device *pdev)
{
int i;
struct i2c_adapter *adapter;
struct i2c_client *newdev;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
/* Assume master adapter zero */
adapter = i2c_get_adapter(0);
if (!adapter) {
dev_err(&pdev->dev, "Failed to get I2C adapter 0.\n");
return -ENODEV;
}
/* Map the I2C mux */
pdata->mux = i2c_new_device(adapter, &pca954x_device_info);
if (!pdata->mux) {
dev_err(&pdev->dev, "Failed to register %s\n",
pca954x_device_info.type);
return -ENODEV;
}
/* Map GPIO expanders */
for (i = 0; i < ARRAY_SIZE(sx150x_devinfo); i++) {
/* Physical bus 4 on switch is virtual bus 5 in system */
adapter = i2c_get_adapter(5 + i);
if (!adapter) {
dev_err(&pdev->dev, "No I2C bus %d.\n", 5 + i);
return -ENODEV;
}
newdev = i2c_new_device(adapter, &(sx150x_devinfo[i]));
if (!newdev) {
dev_err(&pdev->dev, "Bus%d: Device %s missing\n",
5 + i, sx150x_devinfo[0].type);
return -ENODEV;
}
pdata->expander[i] = newdev;
}
/* Now probe the slots */
return green_probe_slots(pdev);
}
/* Unmap MangOH green IoT slots. */
static int mangoh_green_unmap(struct platform_device *pdev)
{
int i;
struct green_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct i2c_adapter *adapter = pdata->mux->adapter;
/* Release gpios first */
green_release_slots(pdev);
for (i = 0; i < ARRAY_SIZE(sx150x_devinfo); i++) {
i2c_unregister_device(pdata->expander[i]);
i2c_put_adapter(pdata->expander[i]->adapter);
}
i2c_unregister_device(pdata->mux);
i2c_put_adapter(adapter);
return 0;
}
/* Release function is needed to avoid warning when device is deleted */
static void mangoh_green_release(struct device *dev) { /* do nothing */ }
struct platform_device mangoh_green = {
.name = "mangoh-green",
.id = -1,
.dev = {
.platform_data = &green_pdata,
.release = mangoh_green_release,
},
};
struct mangoh_desc mangoh_green_desc = {
.type = "mangoh-green",
.map = mangoh_green_map,
.unmap = mangoh_green_unmap,
};
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include "iot-slot-eeprom.h"
#include "iot-slot.h"
#ifndef CONFIG_IOT_SLOT_NUM_SUPPORTED
#define CONFIG_IOT_SLOT_NUM_SUPPORTED 3
#endif
#ifndef CONFIG_IOT_SLOT_MAX_INTERFACES_PER_CARD
#define CONFIG_IOT_SLOT_MAX_INTERFACES_PER_CARD 10
#endif /* CONFIG_IOT_SLOT_MAX_INTERFACES_PER_CARD */
enum iot_slot_interface {
IOT_SLOT_INTERFACE_GPIO,
IOT_SLOT_INTERFACE_I2C,
IOT_SLOT_INTERFACE_SPI,
IOT_SLOT_INTERFACE_USB,
IOT_SLOT_INTERFACE_SDIO,
IOT_SLOT_INTERFACE_ADC,
IOT_SLOT_INTERFACE_PCM,
IOT_SLOT_INTERFACE_CLK,
IOT_SLOT_INTERFACE_UART,
IOT_SLOT_INTERFACE_PLATFORM,
};
struct iot_slot_interface_data {
enum iot_slot_interface type;
union {
struct {
struct i2c_client* client;
} i2c;
struct {
struct spi_device* device;
} spi;
} data;
};
struct iot_slot {
struct platform_device *pdev;
bool card_present;
struct i2c_adapter *i2c_adapter;
unsigned int num_interfaces;
struct iot_slot_interface_data interfaces[
CONFIG_IOT_SLOT_MAX_INTERFACES_PER_CARD];
};
static int iot_slot_add_gpio(struct iot_slot *slot,
struct list_head *gpio_item);
static int iot_slot_add_i2c(struct iot_slot *slot, struct list_head *i2c_item);
static int iot_slot_add_spi(struct iot_slot *slot, struct list_head *spi_item);
static int iot_slot_add_sdio(struct iot_slot *slot,
struct list_head *sdio_item);
static int iot_slot_add_adc(struct iot_slot *slot, struct list_head *adc_item);
static int iot_slot_add_pcm(struct iot_slot *slot, struct list_head *pcm_item);
static int iot_slot_add_uart(struct iot_slot *slot,
struct list_head *uart_item);
static void iot_slot_release_resources(struct iot_slot *slot);
static struct iot_slot *slots[CONFIG_IOT_SLOT_NUM_SUPPORTED];
static struct mutex management_mutex;
static int iot_slot_enumerate(struct iot_slot *slot)
{
int ret = 0;
struct list_head *item;
struct device* device;
struct iot_slot_platform_data *pdata;
int slot_index;
struct i2c_client *eeprom;
device = &slot->pdev->dev;
pdata = dev_get_platdata(device);
slot_index = slot->pdev->id;
slot->card_present =
gpio_get_value_cansleep(pdata->card_detect_gpio) == 0;
if (slot->card_present) {
dev_info(device, "Detected IoT card on slot %d\n", slot_index);
} else {
dev_dbg(device, "No IoT card detected on slot %d\n", slot_index);
goto done;
}
/* Set card detect to output high to activate the eeprom */
ret = gpio_direction_output(pdata->card_detect_gpio, 1);
if (ret != 0) {
dev_err(device, "Couldn't set card detect to output\n");
}
eeprom = eeprom_load(slot->i2c_adapter);
if (eeprom == NULL)
{
dev_warn(device,
"Card detected on IoT slot %d, but no eeprom was detected\n",
slot_index);
goto restore_card_detect;
}
/* Take the IoT card out of reset */
gpio_set_value_cansleep(pdata->reset_gpio, 1);
/*
* Give the IoT card 10ms to get ready before we start accessing it.
*/
msleep(10);
list_for_each(item, eeprom_if_list(eeprom)) {
enum EepromInterface interface_type;
if (slot->num_interfaces >=
CONFIG_IOT_SLOT_MAX_INTERFACES_PER_CARD) {
dev_info(
device,
"Card contains more than the maximum supported number of interfaces (%d)\n",
CONFIG_IOT_SLOT_MAX_INTERFACES_PER_CARD);
ret = -ERANGE;
goto enumeration_fail;
}
interface_type = eeprom_if_type(item);
switch (interface_type) {
case EEPROM_IF_GPIO:
dev_info(device,
"Found GPIO interface specification\n");
ret = iot_slot_add_gpio(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_I2C:
dev_info(device,
"Found I2C interface specification\n");
ret = iot_slot_add_i2c(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_SPI:
dev_info(device,
"Found SPI interface specification\n");
ret = iot_slot_add_spi(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_USB:
dev_info(device, "Found USB interface specification\n");
slot->interfaces[slot->num_interfaces].type =
IOT_SLOT_INTERFACE_CLK;
slot->num_interfaces++;
break;
case EEPROM_IF_SDIO:
dev_info(device,
"Found SDIO interface specification\n");
ret = iot_slot_add_sdio(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_ADC:
dev_info(device, "Found ADC interface specification\n");
ret = iot_slot_add_adc(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_PCM:
dev_info(device, "Found PCM interface specification\n");
ret = iot_slot_add_pcm(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_CLK:
dev_info(device, "Found PPS interface specification\n");
slot->interfaces[slot->num_interfaces].type =
IOT_SLOT_INTERFACE_CLK;
slot->num_interfaces++;
break;
case EEPROM_IF_UART:
dev_info(device,
"Found UART interface specification\n");
ret = iot_slot_add_uart(slot, item);
if (ret != 0) {
goto enumeration_fail;
}
break;
case EEPROM_IF_PLAT:
/* TODO: platorm type isn't properly supported yet */
dev_info(device,
"Found platofrm interface specification\n");
slot->interfaces[slot->num_interfaces].type =
IOT_SLOT_INTERFACE_PLATFORM;
slot->num_interfaces++;
break;
default:
break;
}
}
goto unload_eeprom;
enumeration_fail:
iot_slot_release_resources(slot);
unload_eeprom:
/* Free EEPROM and restore GPIO direction */
eeprom_unload(eeprom);
restore_card_detect:
gpio_direction_input(pdata->card_detect_gpio);
done:
return ret;
}
static int iot_slot_add_gpio(struct iot_slot *slot,
struct list_head *gpio_item)
{
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
int i;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
int slot_index = pdev->id;
int ret = 0;
char gpio_label[32];
for (i = 0; i < 4; i++) {
uint8_t cfg = eeprom_if_gpio_cfg(gpio_item, i);
ret = snprintf(gpio_label, ARRAY_SIZE(gpio_label),
"IoT slot %d gpio %d", slot_index, i);
BUG_ON(ret >= ARRAY_SIZE(gpio_label));
ret = devm_gpio_request_one(device,
pdata->gpio[i],
GPIOF_IN, gpio_label);
if (ret != 0) {
int j;
dev_err(device,
"Couldn't acquire gpio %d on IoT slot %d\n", i,
slot_index);
for (j = 0; j < i; j++) {
devm_gpio_free(device, pdata->gpio[j]);
return -EACCES;
}
}
switch (cfg)
{
case EEPROM_GPIO_CFG_OUTPUT_LOW:
gpio_direction_output(pdata->gpio[i], 0);
break;
case EEPROM_GPIO_CFG_OUTPUT_HIGH:
gpio_direction_output(pdata->gpio[i], 1);
break;
case EEPROM_GPIO_CFG_INPUT_PULL_UP:
gpio_direction_input(pdata->gpio[i]);
gpio_pull_up(pdata->gpio[i]);
break;
case EEPROM_GPIO_CFG_INPUT_PULL_DOWN:
gpio_direction_input(pdata->gpio[i]);
gpio_pull_down(pdata->gpio[i]);
break;
case EEPROM_GPIO_CFG_INPUT_FLOATING:
gpio_direction_input(pdata->gpio[i]);
break;
default:
/* reserved, ignore */
break;
}
}
slot->interfaces[slot->num_interfaces].type = IOT_SLOT_INTERFACE_GPIO;
slot->num_interfaces++;
return 0;
}
static int iot_slot_add_i2c(struct iot_slot *slot, struct list_head *i2c_item)
{
struct platform_device *pdev = slot->pdev;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct i2c_board_info board = {};
int irq_gpio;
/*
* NOTE: There's no need to request the i2c_adapter because that's
* already handled earlier when the eeprom is read.
*/
strncpy(board.type, eeprom_if_i2c_modalias(i2c_item),
sizeof(board.type));
irq_gpio = eeprom_if_i2c_irq_gpio(i2c_item);
if (irq_gpio != IRQ_GPIO_UNUSED)
{
board.irq = gpio_to_irq(pdata->gpio[irq_gpio]);
}
board.addr = eeprom_if_i2c_address(i2c_item);
slot->interfaces[slot->num_interfaces].type = IOT_SLOT_INTERFACE_I2C;
slot->interfaces[slot->num_interfaces].data.i2c.client =
i2c_new_device(slot->i2c_adapter, &board);
if (slot->interfaces[slot->num_interfaces].data.i2c.client == NULL) {
return -EINVAL;
}
slot->num_interfaces++;
return 0;
}
static int iot_slot_add_spi(struct iot_slot *slot, struct list_head *spi_item)
{
int ret = 0;
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct spi_master *spi_master;
int chip_select;
int irq_gpio;
struct spi_board_info board = {
.max_speed_hz = 2*1000*1000,
.mode = SPI_MODE_0,
.platform_data = NULL,
};
struct spi_device *spi_device;
ret = pdata->request_spi(&spi_master, &chip_select);
if (ret != 0) {
dev_err(device, "Couldn't get SPI master\n");
return -ENODEV;
}
board.chip_select = chip_select;
/* Assign IRQ number */
irq_gpio = eeprom_if_spi_irq_gpio(spi_item);
if (irq_gpio != IRQ_GPIO_UNUSED)
{
board.irq = gpio_to_irq(pdata->gpio[irq_gpio]);
}
strncpy(board.modalias, eeprom_if_spi_modalias(spi_item),
sizeof(board.modalias));
spi_device = spi_new_device(spi_master, &board);
if (spi_device == NULL) {
dev_err(device, "Couldn't add new SPI device\n");
pdata->release_spi();
return -ENODEV;
}
slot->interfaces[slot->num_interfaces].type = IOT_SLOT_INTERFACE_SPI;
slot->interfaces[slot->num_interfaces].data.spi.device = spi_device;
slot->num_interfaces++;
return 0;
}
static int iot_slot_add_sdio(struct iot_slot *slot, struct list_head *sdio_item)
{
int ret = 0;
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (pdata->request_sdio) {
ret = pdata->request_sdio();
if (ret != 0)
{
dev_err(device, "Couldn't request the SDIO interface\n");
ret = -ENODEV;
goto done;
}
}
slot->interfaces[slot->num_interfaces].type = IOT_SLOT_INTERFACE_SDIO;
slot->num_interfaces++;
done:
return ret;
}
static int iot_slot_add_adc(struct iot_slot *slot, struct list_head *adc_item)
{
int ret = 0;
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (pdata->request_adc) {
ret = pdata->request_adc();
if (ret != 0)
{
dev_err(device, "Couldn't request the ADC\n");
ret = -ENODEV;
goto done;
}
}
slot->interfaces[slot->num_interfaces].type = IOT_SLOT_INTERFACE_ADC;
slot->num_interfaces++;
done:
return ret;
}
static int iot_slot_add_pcm(struct iot_slot *slot, struct list_head *pcm_item)
{
int ret = 0;
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (pdata->request_pcm) {
ret = pdata->request_pcm();
if (ret != 0)
{
dev_err(device, "Couldn't request the PCM interface\n");
ret = -ENODEV;
goto done;
}
}
slot->interfaces[slot->num_interfaces].type = IOT_SLOT_INTERFACE_PCM;
slot->num_interfaces++;
done:
return ret;
}
static int iot_slot_add_uart(struct iot_slot *slot, struct list_head *uart_item)
{
int ret = 0;
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
struct iot_slot_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (pdata->request_uart) {
/*
* TODO: Should the request_uart be providing some sort of uart handle
* that we can pass to a kernel module?
*/
ret = pdata->request_uart();
if (ret != 0)
{
dev_err(device, "Couldn't request the UART interface\n");
ret = -ENODEV;
goto done;
}
}
slot->interfaces[slot->num_interfaces].type =
IOT_SLOT_INTERFACE_UART;
slot->num_interfaces++;
done:
return ret;
}
static void iot_slot_release_resources(struct iot_slot *slot)
{
struct platform_device *pdev = slot->pdev;
struct device *device = &pdev->dev;
struct iot_slot_platform_data *pdata = dev_get_platdata(device);
int i;
for (i = 0; i < slot->num_interfaces; i++) {
int gpio;
dev_info(device, "On interface %d of type %d\n", i, slot->interfaces[i].type);
switch (slot->interfaces[i].type) {
case IOT_SLOT_INTERFACE_GPIO:
for (gpio = 0; gpio < ARRAY_SIZE(pdata->gpio); gpio++) {
devm_gpio_free(device, pdata->gpio[gpio]);
}
break;
case IOT_SLOT_INTERFACE_I2C:
i2c_unregister_device(
slot->interfaces[i].data.i2c.client);
/*
* NOTE: I2C is released lower down because it is
* acquired to read the eeprom
*/
break;
case IOT_SLOT_INTERFACE_SPI:
spi_unregister_device(
slot->interfaces[i].data.spi.device);
if (pdata->release_spi() != 0)
dev_err(device, "Couldn't release SPI\n");
break;
case IOT_SLOT_INTERFACE_USB:
/* No cleanup required */
break;
case IOT_SLOT_INTERFACE_SDIO:
if (pdata->release_sdio() != 0)
dev_err(device, "Couldn't release SDIO\n");
break;
case IOT_SLOT_INTERFACE_ADC:
if (pdata->release_adc() != 0)
dev_err(device, "Couldn't release ADC\n");
break;
case IOT_SLOT_INTERFACE_PCM:
if (pdata->release_pcm() != 0)
dev_err(device, "Couldn't release PCM\n");
break;
case IOT_SLOT_INTERFACE_CLK:
/* No cleanup required */
break;
case IOT_SLOT_INTERFACE_UART:
if (pdata->release_uart && pdata->release_uart() != 0)
dev_err(device, "Couldn't release UART\n");
break;
case IOT_SLOT_INTERFACE_PLATFORM:
/* TODO: ??? */
break;
default:
dev_err(device,
"Found interface type %d, which is not supported\n",
slot->interfaces[i].type);
break;
}
}
if (slot->i2c_adapter) {
int ret = pdata->release_i2c(&slot->i2c_adapter);
if (ret != 0) {
dev_err(device, "Failed to release i2c adapter");
}
slot->i2c_adapter = NULL;
}
devm_gpio_free(device, pdata->card_detect_gpio);
devm_gpio_free(device, pdata->reset_gpio);
slot->pdev = NULL;
slot->card_present = false;
}
static int iot_slot_request_essential_resources(struct iot_slot *slot)
{
int ret = 0;
struct device* device;
struct iot_slot_platform_data *pdata;
int slot_index;
char gpio_label[32];
device = &slot->pdev->dev;
pdata = dev_get_platdata(device);
slot_index = slot->pdev->id;
ret = snprintf(gpio_label, ARRAY_SIZE(gpio_label), "IoT slot %d reset",
slot_index);
BUG_ON(ret >= ARRAY_SIZE(gpio_label));
ret = devm_gpio_request_one(device, pdata->reset_gpio,
(GPIOF_OUT_INIT_HIGH | GPIOF_ACTIVE_LOW),
gpio_label);
if (ret != 0) {
dev_err(device, "Couldn't acquire reset gpio on IoT slot %d\n",
slot_index);
goto cleanup;
}
ret = snprintf(gpio_label, ARRAY_SIZE(gpio_label),
"IoT slot %d card detect", slot_index);
BUG_ON(ret >= ARRAY_SIZE(gpio_label));
ret = devm_gpio_request_one(device,
pdata->card_detect_gpio,
GPIOF_IN, gpio_label);
if (ret != 0) {
dev_err(device,
"Couldn't acquire card detect gpio on IoT slot %d\n",
slot_index);
goto cleanup;
}
ret = pdata->request_i2c(&slot->i2c_adapter);
if (ret != 0) {
// Couldn't get i2c adapter
goto cleanup;
}
return ret;
cleanup:
iot_slot_release_resources(slot);
return ret;
}
static int iot_slot_probe(struct platform_device *pdev)
{
int ret = 0;
int new_slot_index = pdev->id;
struct device *device = &pdev->dev;
struct iot_slot *slot;
if (new_slot_index < 0 ||
new_slot_index >= CONFIG_IOT_SLOT_NUM_SUPPORTED) {
dev_err(device, "IoT slot has invalid index (%d)\n",
new_slot_index);
ret = -EINVAL;
goto done;
}
mutex_lock(&management_mutex);
if (slots[new_slot_index] != NULL) {
dev_err(device,
"An IoT slot device has already been created for slot %d\n",
new_slot_index);
ret = -EINVAL;
goto unlock;
}
slot = devm_kzalloc(&pdev->dev, sizeof(*slot), GFP_KERNEL);
if (!slot) {
ret = -ENOMEM;
goto unlock;
}
slots[new_slot_index] = slot;
slot->pdev = pdev;
slot->num_interfaces = 0;
platform_set_drvdata(pdev, slot);
ret = iot_slot_request_essential_resources(slot);
if (ret != 0) {
dev_warn(device,
"Couldn't acquire resources required to perform IoT card enumeration\n");
goto cleanup;
}
ret = iot_slot_enumerate(slot);
if (ret != 0)
dev_warn(device,
"IoT card enumeration failed for slot %d\n",
new_slot_index);
cleanup:
if (ret != 0) {
kfree(slot);
slots[new_slot_index] = NULL;
}
unlock:
mutex_unlock(&management_mutex);
done:
return ret;
}
static int iot_slot_remove(struct platform_device *pdev)
{
int ret = 0;
struct iot_slot *slot = platform_get_drvdata(pdev);
const int slot_index = pdev->id;
if (slot_index < 0 || slot_index >= CONFIG_IOT_SLOT_NUM_SUPPORTED) {
dev_err(&pdev->dev, "IoT slot has invalid index (%d)\n",
slot_index);
ret = -EINVAL;
goto done;
}
if (slots[slot_index]->pdev == NULL) {
dev_err(&pdev->dev,
"Tried to remove IoT slot %d, but it does not exist\n",
slot_index);
ret = -EINVAL;
goto done;
}
if (slots[slot_index] != slot || slots[slot_index]->pdev != pdev) {
dev_err(&pdev->dev,
"The platform device which claims to be registered for IoT slot %d does not appear to be the actual device registered\n",
slot_index);
ret = -EINVAL;
goto done;
}
iot_slot_release_resources(slot);
kfree(slot);
slots[slot_index] = NULL;
done:
return ret;
}
static const struct platform_device_id iot_slot_ids[] = {
{"iot-slot", (kernel_ulong_t)0},
{},
};
MODULE_DEVICE_TABLE(platform, iot_slot_ids);
static struct platform_driver iot_slot_driver = {
.probe = iot_slot_probe,
.remove = iot_slot_remove,
.driver = {
.name = "iot-slot",
.owner = THIS_MODULE,
.bus = &platform_bus_type,
},
.id_table = iot_slot_ids,
};
static int __init iot_slot_init(void)
{
mutex_init(&management_mutex);
platform_driver_register(&iot_slot_driver);
return 0;
}
static void __exit iot_slot_exit(void)
{
platform_driver_unregister(&iot_slot_driver);
}
module_init(iot_slot_init);
module_exit(iot_slot_exit);
MODULE_ALIAS("platform:iot-slot");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sierra Wireless");
MODULE_DESCRIPTION("IoT slot management for IoT cards");
MODULE_VERSION("0.2");
......@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*
*/
#ifndef MANGOH_IOT_EEPROM_1V0_H
#define MANGOH_IOT_EEPROM_1V0_H
#ifndef IOT_SLOT_EEPROM_1V0_H
#define IOT_SLOT_EEPROM_1V0_H
typedef struct eeprom_if_reserved_ {
char reserved[63];
......@@ -66,4 +66,4 @@ typedef struct eeprom_if_1v0_ {
#define EEPROM_1V0_INTERFACE_OFFSET 192
#endif /* MANGOH_IOT_EEPROM_1V0_H */
#endif /* IOT_SLOT_EEPROM_1V0_H */
......@@ -55,8 +55,8 @@
#include <linux/gpio/driver.h>
#include <linux/platform_data/at24.h>
#include "eeprom.h"
#include "eeprom_1v0.h"
#include "iot-slot-eeprom.h"
#include "iot-slot-eeprom-1v0.h"
#define HOST_UINT8(buffer, offset) (*(uint8_t*)(&(buffer)[(offset)]))
#define HOST_UINT16(buffer, offset) ntohs(*(uint16_t*)(&(buffer)[(offset)]))
......@@ -66,22 +66,6 @@
#define EEPROM_VERSION_MAJOR(version) (((version) & 0xff00) >> 8)
#define EEPROM_VERSION_MINOR(version) ((version) & 0xff)
typedef enum eeprom_if_t_ {
eeprom_if_gpio = 0,
eeprom_if_i2c = 1,
eeprom_if_spi = 2,
eeprom_if_usb = 3,
eeprom_if_sdio = 4,
eeprom_if_adc = 5,
eeprom_if_pcm = 6,
eeprom_if_clk = 7,
eeprom_if_uart = 8,
eeprom_if_plat = 9,
/* add more interface types here */
eeprom_if_last_supported,
eeprom_if_last = 0xff
} eeprom_if_t;
#define IOT_EEPROM_SIZE 4096
/*
* Map in which EEPROM contents are read. This is global so make sure
......@@ -165,7 +149,7 @@ static void *eeprom_if_first(struct i2c_client *eeprom)
uint8_t *buffer = to_eeprom_buffer(eeprom);
eeprom_if_1v0 *ifc =
(eeprom_if_1v0*)&buffer[EEPROM_1V0_INTERFACE_OFFSET];
return (eeprom_if_last == ifc->type ? NULL : ifc);
return (ifc->type == EEPROM_IF_LAST ? NULL : ifc);
}
default:
BUG();
......@@ -178,7 +162,7 @@ static void *eeprom_if_next(struct i2c_client *eeprom, void *prev)
switch (eeprom_version(eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *ifc = (eeprom_if_1v0*)prev;
return (eeprom_if_last == ifc->type ? NULL : ++ifc);
return (ifc->type == EEPROM_IF_LAST ? NULL : ++ifc);
}
default:
BUG();
......@@ -219,18 +203,19 @@ static int eeprom_load_interfaces(struct i2c_client *eeprom)
}
/* Public functions */
struct i2c_client *eeprom_load(int slot)
struct i2c_client *eeprom_load(struct i2c_adapter *i2c_adapter)
{
struct i2c_adapter *adapter;
struct i2c_client *eeprom;
adapter = i2c_get_adapter(1 + slot);
if (!adapter)
if (!i2c_adapter)
return NULL;
/* This automatically runs the setup() function */
eeprom = i2c_new_device(adapter, &at24_eeprom_info);
i2c_put_adapter(adapter);
eeprom = i2c_new_device(i2c_adapter, &at24_eeprom_info);
/* If no eeprom is detected, return NULL */
if (eeprom == NULL)
return NULL;
/* Validate EEPROM header */
if (!to_eeprom_buffer(eeprom)) {
......@@ -256,44 +241,39 @@ int eeprom_num_slots(struct i2c_client *eeprom)
return 1; /* for now */
}
#define __DECLARE_IS_IF(bus) \
__DECLARE_IS_IF_PROTOTYPE(bus, i) { \
struct eeprom_if_map *m = to_eeprom_if_map(i); \
struct i2c_client *eeprom = m->eeprom; \
switch (eeprom_version(eeprom)) { \
case EEPROM_VERSION(1, 0): { \
eeprom_if_1v0 *i = (eeprom_if_1v0*)m->contents; \
return (eeprom_if_##bus == i->type); \
} \
default: \
BUG(); \
return 0; \
} \
}
__DECLARE_IS_IF(gpio)
__DECLARE_IS_IF(i2c)
__DECLARE_IS_IF(spi)
__DECLARE_IS_IF(usb)
__DECLARE_IS_IF(sdio)
__DECLARE_IS_IF(adc)
__DECLARE_IS_IF(pcm)
__DECLARE_IS_IF(clk)
__DECLARE_IS_IF(uart)
__DECLARE_IS_IF(plat)
struct list_head *eeprom_if_list(struct i2c_client *eeprom)
{
return to_eeprom_if_list(eeprom);
}
enum EepromInterface eeprom_if_type(struct list_head *item)
{
enum EepromInterface result;
struct eeprom_if_map *m = to_eeprom_if_map(item);
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0):
{
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
result = eif->type;
break;
}
default:
BUG();
result = EEPROM_IF_LAST;
break;
}
return result;
}
uint8_t eeprom_if_gpio_cfg(struct list_head *item, unsigned int pin)
{
struct eeprom_if_map *m = to_eeprom_if_map(item);
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
BUG_ON(eeprom_if_gpio != eif->type);
BUG_ON(eif->type != EEPROM_IF_GPIO);
BUG_ON(pin >= ARRAY_SIZE(eif->ifc.gpio.cfg));
return eif->ifc.gpio.cfg[pin];
}
......@@ -309,7 +289,7 @@ char *eeprom_if_spi_modalias(struct list_head *item)
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
BUG_ON(eeprom_if_spi != eif->type);
BUG_ON(eif->type != EEPROM_IF_SPI);
return (eif->ifc.spi.modalias);
}
default:
......@@ -325,7 +305,7 @@ int eeprom_if_spi_irq_gpio(struct list_head *item)
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
BUG_ON(eeprom_if_spi != eif->type);
BUG_ON(eif->type != EEPROM_IF_SPI);
return eif->ifc.spi.irq_gpio;
}
default:
......@@ -341,7 +321,7 @@ char *eeprom_if_i2c_modalias(struct list_head *item)
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
BUG_ON(eeprom_if_i2c != eif->type);
BUG_ON(eif->type != EEPROM_IF_I2C);
return (eif->ifc.i2c.modalias);
}
default:
......@@ -357,7 +337,7 @@ int eeprom_if_i2c_irq_gpio(struct list_head *item)
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
BUG_ON(eeprom_if_i2c != eif->type);
BUG_ON(eif->type != EEPROM_IF_I2C);
return eif->ifc.i2c.irq_gpio;
}
default:
......@@ -373,7 +353,7 @@ uint8_t eeprom_if_i2c_address(struct list_head *item)
switch (eeprom_version(m->eeprom)) {
case EEPROM_VERSION(1, 0): {
eeprom_if_1v0 *eif = (eeprom_if_1v0*)m->contents;
BUG_ON(eeprom_if_i2c != eif->type);
BUG_ON(eif->type != EEPROM_IF_I2C);
return (eif->ifc.i2c.address);
}
default:
......
......@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*
*/
#ifndef MANGOH_IOT_EEPROM_H
#define MANGOH_IOT_EEPROM_H
#ifndef IOT_SLOT_EEPROM_H
#define IOT_SLOT_EEPROM_H
#include <linux/i2c.h>
......@@ -24,29 +24,36 @@
#define EEPROM_GPIO_CFG_OUTPUT_LOW (0x4)
#define EEPROM_GPIO_CFG_OUTPUT_HIGH (0x5)
struct i2c_client *eeprom_load(int slot);
enum EepromInterface
{
EEPROM_IF_GPIO,
EEPROM_IF_I2C,
EEPROM_IF_SPI,
EEPROM_IF_USB,
EEPROM_IF_SDIO,
EEPROM_IF_ADC,
EEPROM_IF_PCM,
EEPROM_IF_CLK,
EEPROM_IF_UART,
EEPROM_IF_PLAT,
/* add more interface types here */
EEPROM_IF_LAST_SUPPORTED,
EEPROM_IF_LAST = 0xFF,
};
struct i2c_client *eeprom_load(struct i2c_adapter *i2c_adapter);
void eeprom_unload(struct i2c_client *eeprom);
struct list_head *eeprom_if_list(struct i2c_client *eeprom);
int eeprom_num_slots(struct i2c_client *eeprom);
#define __DECLARE_IS_IF_PROTOTYPE(bus, ifc) \
bool eeprom_is_if_##bus(struct list_head *ifc)
__DECLARE_IS_IF_PROTOTYPE(gpio, ifc);
__DECLARE_IS_IF_PROTOTYPE(i2c, ifc);
__DECLARE_IS_IF_PROTOTYPE(spi, ifc);
__DECLARE_IS_IF_PROTOTYPE(usb, ifc);
__DECLARE_IS_IF_PROTOTYPE(sdio, ifc);
__DECLARE_IS_IF_PROTOTYPE(adc, ifc);
__DECLARE_IS_IF_PROTOTYPE(pcm, ifc);
__DECLARE_IS_IF_PROTOTYPE(clk, ifc);
__DECLARE_IS_IF_PROTOTYPE(uart, ifc);
__DECLARE_IS_IF_PROTOTYPE(plat, ifc);
enum EepromInterface eeprom_if_type(struct list_head *item);
uint8_t eeprom_if_gpio_cfg(struct list_head *item, unsigned int pin);
char *eeprom_if_spi_modalias(struct list_head *item);
int eeprom_if_spi_irq_gpio(struct list_head *item);
char *eeprom_if_i2c_modalias(struct list_head *item);
int eeprom_if_i2c_irq_gpio(struct list_head *item);
uint8_t eeprom_if_i2c_address(struct list_head *item);
#endif /* MANGOH_IOT_EEPROM_H */
#endif /* IOT_SLOT_EEPROM_H */
#ifndef IOT_SLOT_H
#define IOT_SLOT_H
struct spi_master;
struct i2c_adapter;
/*
* TODO:
* Need to decide whether an absent function pointer means that no action is
* needed to acquire the interface or that the interface is not supported on the
* IoT slot. I think not supported makes more sense unless we separate out
* muxing from getting. Eg. have a require_x, get_x, release_x functions.
*/
struct iot_slot_platform_data {
int gpio[4];
int reset_gpio;
int card_detect_gpio;
int (*request_i2c)(struct i2c_adapter **adapter);
int (*release_i2c)(struct i2c_adapter **adapter);
int (*request_spi)(struct spi_master **spi_master, int *cs);
int (*release_spi)(void);
/* TODO: what output param(s) for uart? */
int (*request_uart)(void);
int (*release_uart)(void);
/* TODO: how are adc's managed in the kernel? */
int (*request_adc)(void);
int (*release_adc)(void);
/* TODO: output params? */
int (*request_sdio)(void);
int (*release_sdio)(void);
/* TODO: output params? */
int (*request_pcm)(void);
int (*release_pcm)(void);
};
#endif /* IOT_SLOT_H */
/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef MANGOH_IOT_MANGOH_H
#define MANGOH_IOT_MANGOH_H
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
typedef enum {
mangoh_bus_i2c,
mangoh_bus_spi,
mangoh_bus_uart,
mangoh_bus_sdio,
mangoh_bus_usb,
mangoh_bus_gpio,
mangoh_bus_pcm,
mangoh_bus_adc,
mangoh_bus_last,
} mangoh_bus_t;
struct mangoh_desc {
char *type;
int (*map)(struct platform_device *pdev);
int (*unmap)(struct platform_device *pdev);
};
extern struct mangoh_desc mangoh_green_desc;
extern struct platform_device mangoh_green;
#endif /* MANGOH_IOT_MANGOH_H */
/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* IoT expansion platform driver for Sierra Wireless MangOH board(s).
* Currently supporting only MangOH green platform.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include "mangoh.h"
static char *model = "green";
module_param(model, charp, S_IRUGO);
MODULE_PARM_DESC(model, "MangOH board model.");
static const struct platform_device_id mangoh_iot_ids[] = {
{"mangoh-green", (kernel_ulong_t)&mangoh_green_desc},
{},
};
MODULE_DEVICE_TABLE(platform, mangoh_iot_ids);
static int mangoh_iot_probe(struct platform_device *pdev)
{
struct mangoh_desc *desc;
desc = (struct mangoh_desc *)platform_get_device_id(pdev)->driver_data;
if (!desc || !desc->map)
return -ENODEV;
platform_set_drvdata(pdev, desc);
return desc->map(pdev);
}
static int mangoh_iot_remove(struct platform_device *pdev)
{
struct mangoh_desc *desc = platform_get_drvdata(pdev);
if (desc && desc->unmap)
desc->unmap(pdev);
/* desc will be freed with device removal, so we're done */
dev_info(&pdev->dev, "Removed.\n");
return 0;
}
static struct platform_driver mangoh_iot_driver = {
.probe = mangoh_iot_probe,
.remove = mangoh_iot_remove,
.driver = {
.name = "mangoh-iot",
.owner = THIS_MODULE,
.bus = &platform_bus_type,
},
.id_table = mangoh_iot_ids,
};
static int __init mangoh_iot_init(void)
{
struct platform_device *pdev;
if (!strcasecmp(model, "green"))
pdev = &mangoh_green;
else
pdev = NULL;
if (!pdev) {
pr_err("%s: unknown model 'mangoh-%s'.\n", __func__, model);
return -ENODEV;
}
platform_driver_register(&mangoh_iot_driver);
if (platform_device_register(pdev)) {
platform_driver_unregister(&mangoh_iot_driver);
return -ENODEV;
}
return 0;
}
static void __exit mangoh_iot_exit(void)
{
platform_device_unregister(&mangoh_green);
platform_driver_unregister(&mangoh_iot_driver);
}
module_init(mangoh_iot_init);
module_exit(mangoh_iot_exit);
MODULE_ALIAS("platform:mangoh-iot");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sierra Wireless");
MODULE_DESCRIPTION("Linux driver for MangOH IoT expander");
MODULE_VERSION("0.1");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment