greybus: endo: Add code to validate Endo ID

Endo is described by a 16 bit value. Which represents the properties of
modules, interface and ribs on front and back of endo.

This 16 bit value can be used to find all possible pairs of modules and
interfaces and creating modules based on that.

This patch provides helpers to parse 16 bit Endo ID.

(Based on original code written by Alex Elder.)

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Viresh Kumar 2015-05-08 12:58:50 +05:30 committed by Greg Kroah-Hartman
parent 61c0926eec
commit 4f4cc1bf07
2 changed files with 317 additions and 0 deletions

View file

@ -9,6 +9,26 @@
#include "greybus.h"
/* Endo ID (16 bits long) Masks */
#define ENDO_ID_MASK 0xFFFF
#define ENDO_LARGE_MASK 0x1000
#define ENDO_MEDIUM_MASK 0x0400
#define ENDO_MINI_MASK 0x0100
#define ENDO_FRONT_MASK(id) ((id) >> 13)
#define ENDO_BACK_SIDE_RIBS_MASK(ribs) ((1 << (ribs)) - 1)
/*
* endo_is_medium() should be used only if endo isn't large. And endo_is_mini()
* should be used only if endo isn't large or medium.
*/
#define endo_is_large(id) ((id) & ENDO_LARGE_MASK)
#define endo_is_medium(id) ((id) & ENDO_MEDIUM_MASK)
#define endo_is_mini(id) ((id) & ENDO_MINI_MASK)
#define endo_back_left_ribs(id, ribs) (((id) >> (ribs)) & ENDO_BACK_SIDE_RIBS_MASK(ribs))
#define endo_back_right_ribs(id, ribs) ((id) & ENDO_BACK_SIDE_RIBS_MASK(ribs))
/* endo sysfs attributes */
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
@ -55,6 +75,280 @@ struct device_type greybus_endo_type = {
};
/* Validate Endo ID */
/*
* The maximum module height is 2 units. This means any adjacent pair of bits
* in the left or right mask must have at least one bit set.
*/
static inline bool modules_oversized(unsigned int count, unsigned int mask)
{
int i;
for (i = 0; i < count - 1; i++)
if (!(mask & (0x3 << i)))
return true;
return false;
}
/* Reverse a number of least significant bits in a value */
static u8 reverse_bits(unsigned int value, unsigned int bits)
{
u8 result = 0;
u8 result_mask = 1 << (bits - 1);
u8 value_mask = 1;
while (value && result_mask) {
if (value & value_mask) {
result |= result_mask;
value ^= value_mask;
}
value_mask <<= 1;
result_mask >>= 1;
}
return result;
}
/*
* An Endo can have at most one instance of a single rib spanning its whole
* width. That is, the left and right bit masks representing the rib positions
* must have at most one bit set in both masks.
*/
static bool single_cross_rib(u8 left_ribs, u8 right_ribs)
{
u8 span_ribs = left_ribs & right_ribs;
/* Power of 2 ? */
if (span_ribs & (span_ribs - 1))
return false;
return true;
}
/*
* Each Endo size has its own set of front module configurations. For most, the
* resulting rib mask is the same regardless of the Endo size. The mini Endo
* has a few differences though.
*
* Endo front has 4 interface blocks and 3 rib positions. A maximum of 2 ribs
* are allowed to be present for any endo type.
*
* This routine validates front mask and sets 'front_ribs', its 3 least
* significant bits represent front ribs mask, other are 0. The front values
* should be within range (1..6).
*
* front_ribs bitmask:
* - Bit 0: 1st rib location from top, i.e. between interface 1 and 2.
* - Bit 1: 2nd rib location from top, i.e. between interface 2 and 3.
* - Bit 2: 3rd rib location from top, i.e. between interface 3 and 4.
*/
static bool validate_front_ribs(struct greybus_host_device *hd,
struct endo_layout *layout, bool mini,
u16 endo_id)
{
u8 front_mask = ENDO_FRONT_MASK(endo_id);
/* Verify front endo mask is in valid range, i.e. 1-6 */
switch (front_mask) {
case 1:
layout->front_ribs = 0x0;
break;
case 2:
layout->front_ribs = 0x1;
break;
case 3:
layout->front_ribs = 0x4;
break;
case 4:
layout->front_ribs = mini ? 0x2 : 0x3;
break;
case 5:
layout->front_ribs = mini ? 0x2 : 0x6;
break;
case 6:
layout->front_ribs = 0x5;
break;
default:
dev_err(hd->parent,
"%s: Invalid endo front mask 0x%02x, id 0x%04x\n",
__func__, front_mask, endo_id);
return false;
}
return true;
}
/*
* The rear of an endo has a single vertical "spine", and the modules placed on
* the left and right of that spine are separated by ribs. Only one "cross"
* (i.e. rib that spans the entire width) is allowed of the back of the endo;
* all other ribs reach from the spine to the left or right edge.
*
* The width of the module positions on the left and right of the spine are
* determined by the width of the endo (either 1 or 2 "units"). The height of
* the modules is determined by the placement of the ribs (a module can be
* either 1 or 2 units high).
*
* The lower 13 bits of the 16-bit endo id are used to encode back ribs
* information. The large form factor endo uses all of these bits; the medium
* and mini form factors leave some bits unused (such bits shall be ignored, and
* are 0 for the purposes of this endo id definition).
*
* Each defined bit represents a rib position on one or the other side
* of the spine on the back of an endo. If that bit is set (1), it
* means a rib is present in the corresponding location; otherwise
* there is no rib there.
*
* Rotating an endo 180 degrees does not produce a new rib configuration. A
* single endo id represents a specific configuration of ribs without regard to
* its rotational orientation. We define one canonical id to represent a
* particular endo configuration.
*/
static bool validate_back_ribs(struct greybus_host_device *hd,
struct endo_layout *layout, u16 endo_id)
{
u8 max_ribs = layout->max_ribs;
u8 left_ribs;
u8 right_ribs;
/* Extract the left and right rib masks */
left_ribs = endo_back_left_ribs(endo_id, max_ribs);
right_ribs = endo_back_right_ribs(endo_id, max_ribs);
if (!single_cross_rib(left_ribs, right_ribs)) {
dev_err(hd->parent,
"%s: More than one spanning rib (left 0x%02x right 0x%02x), id 0x%04x\n",
__func__, left_ribs, right_ribs, endo_id);
return false;
}
if (modules_oversized(max_ribs, left_ribs)) {
dev_err(hd->parent,
"%s: Oversized module (left) 0x%02x, id 0x%04x\n",
__func__, left_ribs, endo_id);
return false;
}
if (modules_oversized(max_ribs, right_ribs)) {
dev_err(hd->parent,
"%s: Oversized module (Right) 0x%02x, id 0x%04x\n",
__func__, right_ribs, endo_id);
return false;
}
/*
* The Endo numbering scheme represents the left and right rib
* configuration in a way that's convenient for looking for multiple
* spanning ribs. But it doesn't match the normal Endo interface
* numbering scheme (increasing counter-clockwise around the back).
* Reverse the right bit positions so they do match.
*/
right_ribs = reverse_bits(right_ribs, max_ribs);
/*
* A mini or large Endo rotated 180 degrees is still the same Endo. In
* most cases that allows two distinct values to represent the same
* Endo; we choose one of them to be the canonical one (and the other is
* invalid). The canonical one is identified by higher value of left
* ribs mask.
*
* This doesn't apply to medium Endos, because the left and right sides
* are of different widths.
*/
if (max_ribs != ENDO_BACK_RIBS_MEDIUM && left_ribs < right_ribs) {
dev_err(hd->parent, "%s: Non-canonical endo id 0x%04x\n", __func__,
endo_id);
return false;
}
layout->left_ribs = left_ribs;
layout->right_ribs = right_ribs;
return true;
}
/*
* Validate the endo-id passed from SVC. Error out if its not a valid Endo,
* else return structure representing ribs positions on front and back of Endo.
*/
static int gb_validate_endo_id(struct greybus_host_device *hd,
struct endo_layout *layout, u16 endo_id)
{
/* Validate Endo Size */
if (endo_is_large(endo_id)) {
/* Large Endo type */
layout->max_ribs = ENDO_BACK_RIBS_LARGE;
} else if (endo_is_medium(endo_id)) {
/* Medium Endo type */
layout->max_ribs = ENDO_BACK_RIBS_MEDIUM;
} else if (endo_is_mini(endo_id)) {
/* Mini Endo type */
layout->max_ribs = ENDO_BACK_RIBS_MINI;
} else {
dev_err(hd->parent, "%s: Invalid endo type, id 0x%04x\n",
__func__, endo_id);
return -EINVAL;
}
if (!validate_back_ribs(hd, layout, endo_id))
return -EINVAL;
if (!validate_front_ribs(hd, layout,
layout->max_ribs == ENDO_BACK_RIBS_MINI,
endo_id))
return -EINVAL;
return 0;
}
/*
* Look up which module contains the given interface.
*
* A module's ID is the same as its lowest-numbered interface ID. So the module
* ID for a 1x1 module is always the same as its interface ID.
*
* For Endo Back:
* The module ID for an interface on a 1x2 or 2x2 module (which use two
* interface blocks) can be either the interface ID, or one less than the
* interface ID if there is no rib "above" the interface.
*
* For Endo Front:
* There are three rib locations in front and all of them might be unused, i.e.
* a single module is used for all 4 interfaces. We need to check all ribs in
* that case to find module ID.
*/
u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id)
{
struct endo_layout *layout = &endo->layout;
unsigned int height = layout->max_ribs + 1;
unsigned int iid = interface_id - 1;
unsigned int mask, rib_mask;
if (!interface_id)
return 0;
if (iid < height) { /* back left */
mask = layout->left_ribs;
} else if (iid < 2 * height) { /* back right */
mask = layout->right_ribs;
iid -= height;
} else { /* front */
mask = layout->front_ribs;
iid -= 2 * height;
}
/*
* Find the next rib *above* this interface to determine the lowest
* interface ID in the module.
*/
rib_mask = 1 << iid;
while ((rib_mask >>= 1) != 0 && !(mask & rib_mask))
--interface_id;
return interface_id;
}
/*
* Endo "types" have different module locations, these are tables based on those
* types that list the module ids for the different locations.

View file

@ -15,7 +15,28 @@ struct gb_svc {
u8 version[10];
};
/* Max ribs per Endo size */
#define ENDO_BACK_RIBS_MINI 0x4
#define ENDO_BACK_RIBS_MEDIUM 0x5
#define ENDO_BACK_RIBS_LARGE 0x6
/**
* struct endo_layout - represents front/back ribs of the endo.
*
* @front_ribs: Mask of present ribs in front.
* @left_ribs: Mask of present ribs in back (left).
* @right_ribs: Mask of present ribs in back (right).
* @max_ribs: Max ribs on endo back, possible values defined above.
*/
struct endo_layout {
u8 front_ribs;
u8 left_ribs;
u8 right_ribs;
u8 max_ribs;
};
struct gb_endo {
struct endo_layout layout;
struct device dev;
struct gb_svc svc;
u16 id;
@ -29,4 +50,6 @@ struct greybus_host_device;
struct gb_endo *gb_endo_create(struct greybus_host_device *hd);
void gb_endo_remove(struct gb_endo *endo);
u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id);
#endif /* __ENDO_H */