mirror of https://gitee.com/openkylin/linux.git
greybus: spi: add master and device config operations
Add master and device config operations, one is to merge all the master operations and the device config will allow to fetch and add devices for each chip select. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
b455c84655
commit
b343f6afd7
|
@ -651,30 +651,29 @@ struct gb_pwm_disable_request {
|
|||
#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */
|
||||
|
||||
/* Greybus spi operation types */
|
||||
#define GB_SPI_TYPE_MODE 0x02
|
||||
#define GB_SPI_TYPE_FLAGS 0x03
|
||||
#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04
|
||||
#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05
|
||||
#define GB_SPI_TYPE_TRANSFER 0x06
|
||||
#define GB_SPI_TYPE_MASTER_CONFIG 0x02
|
||||
#define GB_SPI_TYPE_DEVICE_CONFIG 0x03
|
||||
#define GB_SPI_TYPE_TRANSFER 0x04
|
||||
|
||||
/* mode request has no payload */
|
||||
struct gb_spi_mode_response {
|
||||
__le16 mode;
|
||||
} __packed;
|
||||
|
||||
/* flags request has no payload */
|
||||
struct gb_spi_flags_response {
|
||||
__le16 flags;
|
||||
} __packed;
|
||||
|
||||
/* bits-per-word request has no payload */
|
||||
struct gb_spi_bpw_response {
|
||||
struct gb_spi_master_config_response {
|
||||
__le32 bits_per_word_mask;
|
||||
__le32 min_speed_hz;
|
||||
__le32 max_speed_hz;
|
||||
__le16 mode;
|
||||
__le16 flags;
|
||||
__le16 num_chipselect;
|
||||
} __packed;
|
||||
|
||||
/* num-chipselects request has no payload */
|
||||
struct gb_spi_chipselect_response {
|
||||
__le16 num_chipselect;
|
||||
struct gb_spi_device_config_request {
|
||||
__le16 chip_select;
|
||||
} __packed;
|
||||
|
||||
struct gb_spi_device_config_response {
|
||||
__le16 mode;
|
||||
__u8 bits_per_word;
|
||||
__le32 max_speed_hz;
|
||||
__u8 name[32];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* SPI bridge driver for the Greybus "generic" SPI module.
|
||||
*
|
||||
* Copyright 2014 Google Inc.
|
||||
* Copyright 2014 Linaro Ltd.
|
||||
* Copyright 2014-2015 Google Inc.
|
||||
* Copyright 2014-2015 Linaro Ltd.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
@ -17,30 +17,20 @@
|
|||
|
||||
struct gb_spi {
|
||||
struct gb_connection *connection;
|
||||
|
||||
/* Modes supported by spi controller */
|
||||
u16 mode;
|
||||
/* constraints of the spi controller */
|
||||
u16 flags;
|
||||
|
||||
/*
|
||||
* copied from kernel:
|
||||
*
|
||||
* A mask indicating which values of bits_per_word are supported by the
|
||||
* controller. Bit n indicates that a bits_per_word n+1 is suported. If
|
||||
* set, the SPI core will reject any transfer with an unsupported
|
||||
* bits_per_word. If not set, this value is simply ignored, and it's up
|
||||
* to the individual driver to perform any validation.
|
||||
*/
|
||||
u32 bits_per_word_mask;
|
||||
|
||||
/*
|
||||
* chipselects will be integral to many controllers; some others might
|
||||
* use board-specific GPIOs.
|
||||
*/
|
||||
u16 num_chipselect;
|
||||
u32 min_speed_hz;
|
||||
u32 max_speed_hz;
|
||||
struct spi_device *spi_devices;
|
||||
};
|
||||
|
||||
static struct spi_master *get_master_from_spi(struct gb_spi *spi)
|
||||
{
|
||||
return spi->connection->private;
|
||||
}
|
||||
|
||||
/* Routines to transfer data */
|
||||
static struct gb_operation *
|
||||
gb_spi_operation_create(struct gb_connection *connection,
|
||||
|
@ -181,7 +171,7 @@ static void gb_spi_cleanup(struct spi_device *spi)
|
|||
}
|
||||
|
||||
|
||||
/* Routines to get controller infomation */
|
||||
/* Routines to get controller information */
|
||||
|
||||
/*
|
||||
* Map Greybus spi mode bits/flags/bpw into Linux ones.
|
||||
|
@ -190,103 +180,86 @@ static void gb_spi_cleanup(struct spi_device *spi)
|
|||
#define gb_spi_mode_map(mode) mode
|
||||
#define gb_spi_flags_map(flags) flags
|
||||
|
||||
static int gb_spi_mode_operation(struct gb_spi *spi)
|
||||
static int gb_spi_get_master_config(struct gb_spi *spi)
|
||||
{
|
||||
struct gb_spi_mode_response response;
|
||||
u16 mode;
|
||||
struct gb_spi_master_config_response response;
|
||||
u16 mode, flags;
|
||||
int ret;
|
||||
|
||||
ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MODE,
|
||||
ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MASTER_CONFIG,
|
||||
NULL, 0, &response, sizeof(response));
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mode = le16_to_cpu(response.mode);
|
||||
spi->mode = gb_spi_mode_map(mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gb_spi_flags_operation(struct gb_spi *spi)
|
||||
{
|
||||
struct gb_spi_flags_response response;
|
||||
u16 flags;
|
||||
int ret;
|
||||
|
||||
ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_FLAGS,
|
||||
NULL, 0, &response, sizeof(response));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
flags = le16_to_cpu(response.flags);
|
||||
spi->flags = gb_spi_flags_map(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gb_spi_bpw_operation(struct gb_spi *spi)
|
||||
{
|
||||
struct gb_spi_bpw_response response;
|
||||
int ret;
|
||||
|
||||
ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_BITS_PER_WORD_MASK,
|
||||
NULL, 0, &response, sizeof(response));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gb_spi_chipselect_operation(struct gb_spi *spi)
|
||||
{
|
||||
struct gb_spi_chipselect_response response;
|
||||
int ret;
|
||||
|
||||
ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_NUM_CHIPSELECT,
|
||||
NULL, 0, &response, sizeof(response));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi->num_chipselect = le16_to_cpu(response.num_chipselect);
|
||||
|
||||
spi->min_speed_hz = le32_to_cpu(response.min_speed_hz);
|
||||
spi->max_speed_hz = le32_to_cpu(response.max_speed_hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gb_spi_setup_device(struct gb_spi *spi, uint16_t cs)
|
||||
{
|
||||
struct spi_master *master = get_master_from_spi(spi);
|
||||
struct gb_spi_device_config_request request;
|
||||
struct gb_spi_device_config_response response;
|
||||
struct spi_board_info spi_board = { {0} };
|
||||
struct spi_device *spidev = &spi->spi_devices[cs];
|
||||
int ret;
|
||||
|
||||
request.chip_select = cpu_to_le16(cs);
|
||||
|
||||
ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_DEVICE_CONFIG,
|
||||
&request, sizeof(request),
|
||||
&response, sizeof(response));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memcpy(spi_board.modalias, response.name, sizeof(spi_board.modalias));
|
||||
spi_board.mode = le16_to_cpu(response.mode);
|
||||
spi_board.bus_num = master->bus_num;
|
||||
spi_board.chip_select = cs;
|
||||
spi_board.max_speed_hz = le32_to_cpu(response.max_speed_hz);
|
||||
|
||||
spidev = spi_new_device(master, &spi_board);
|
||||
if (!spidev)
|
||||
ret = -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the spi device. This includes verifying we can support it (based
|
||||
* on the protocol version it advertises). If that's OK, we get and cached its
|
||||
* mode bits & flags.
|
||||
*/
|
||||
static int gb_spi_init(struct gb_spi *spi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* mode never changes, just get it once */
|
||||
ret = gb_spi_mode_operation(spi);
|
||||
/* get master configuration */
|
||||
ret = gb_spi_get_master_config(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* flags never changes, just get it once */
|
||||
ret = gb_spi_flags_operation(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
spi->spi_devices = kcalloc(spi->num_chipselect,
|
||||
sizeof(struct spi_device), GFP_KERNEL);
|
||||
if (!spi->spi_devices)
|
||||
return -ENOMEM;
|
||||
|
||||
/* total number of chipselects never changes, just get it once */
|
||||
ret = gb_spi_chipselect_operation(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* bits-per-word-mask never changes, just get it once */
|
||||
return gb_spi_bpw_operation(spi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int gb_spi_connection_init(struct gb_connection *connection)
|
||||
{
|
||||
struct gb_spi *spi;
|
||||
struct spi_master *master;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* Allocate master with space for data */
|
||||
master = spi_alloc_master(&connection->bundle->dev, sizeof(*spi));
|
||||
|
@ -315,8 +288,15 @@ static int gb_spi_connection_init(struct gb_connection *connection)
|
|||
master->transfer_one_message = gb_spi_transfer_one_message;
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
/* now, fetch the devices configuration */
|
||||
for (i = 0; i < spi->num_chipselect; i++) {
|
||||
ret = gb_spi_setup_device(spi, i);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
out_err:
|
||||
spi_master_put(master);
|
||||
|
|
Loading…
Reference in New Issue