i2c/powermac: Improve detection of devices from device-tree
This patch adds a number of workarounds for broken Apple device-trees mostly around sound chips. It handles creating the missing audio codec devices and works around various issues with missing addresses or missing compatible properties. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
e1612de9e4
commit
3a3dd0186f
|
@ -227,28 +227,138 @@ static int __devexit i2c_powermac_remove(struct platform_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 __devinit i2c_powermac_get_addr(struct i2c_adapter *adap,
|
||||||
|
struct pmac_i2c_bus *bus,
|
||||||
|
struct device_node *node)
|
||||||
|
{
|
||||||
|
const __be32 *prop;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
/* First check for valid "reg" */
|
||||||
|
prop = of_get_property(node, "reg", &len);
|
||||||
|
if (prop && (len >= sizeof(int)))
|
||||||
|
return (be32_to_cpup(prop) & 0xff) >> 1;
|
||||||
|
|
||||||
|
/* Then check old-style "i2c-address" */
|
||||||
|
prop = of_get_property(node, "i2c-address", &len);
|
||||||
|
if (prop && (len >= sizeof(int)))
|
||||||
|
return (be32_to_cpup(prop) & 0xff) >> 1;
|
||||||
|
|
||||||
|
/* Now handle some devices with missing "reg" properties */
|
||||||
|
if (!strcmp(node->name, "cereal"))
|
||||||
|
return 0x60;
|
||||||
|
else if (!strcmp(node->name, "deq"))
|
||||||
|
return 0x34;
|
||||||
|
|
||||||
|
dev_warn(&adap->dev, "No i2c address for %s\n", node->full_name);
|
||||||
|
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __devinit i2c_powermac_create_one(struct i2c_adapter *adap,
|
||||||
|
const char *type,
|
||||||
|
u32 addr)
|
||||||
|
{
|
||||||
|
struct i2c_board_info info = {};
|
||||||
|
struct i2c_client *newdev;
|
||||||
|
|
||||||
|
strncpy(info.type, type, sizeof(info.type));
|
||||||
|
info.addr = addr;
|
||||||
|
newdev = i2c_new_device(adap, &info);
|
||||||
|
if (!newdev)
|
||||||
|
dev_err(&adap->dev,
|
||||||
|
"i2c-powermac: Failure to register missing %s\n",
|
||||||
|
type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __devinit i2c_powermac_add_missing(struct i2c_adapter *adap,
|
||||||
|
struct pmac_i2c_bus *bus,
|
||||||
|
bool found_onyx)
|
||||||
|
{
|
||||||
|
struct device_node *busnode = pmac_i2c_get_bus_node(bus);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Check for the onyx audio codec */
|
||||||
|
#define ONYX_REG_CONTROL 67
|
||||||
|
if (of_device_is_compatible(busnode, "k2-i2c") && !found_onyx) {
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
|
||||||
|
rc = i2c_smbus_xfer(adap, 0x46, 0, I2C_SMBUS_READ,
|
||||||
|
ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA,
|
||||||
|
&data);
|
||||||
|
if (rc >= 0)
|
||||||
|
i2c_powermac_create_one(adap, "MAC,pcm3052", 0x46);
|
||||||
|
|
||||||
|
rc = i2c_smbus_xfer(adap, 0x47, 0, I2C_SMBUS_READ,
|
||||||
|
ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA,
|
||||||
|
&data);
|
||||||
|
if (rc >= 0)
|
||||||
|
i2c_powermac_create_one(adap, "MAC,pcm3052", 0x47);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __devinit i2c_powermac_get_type(struct i2c_adapter *adap,
|
||||||
|
struct device_node *node,
|
||||||
|
u32 addr, char *type, int type_size)
|
||||||
|
{
|
||||||
|
char tmp[16];
|
||||||
|
|
||||||
|
/* Note: we to _NOT_ want the standard
|
||||||
|
* i2c drivers to match with any of our powermac stuff
|
||||||
|
* unless they have been specifically modified to handle
|
||||||
|
* it on a case by case basis. For example, for thermal
|
||||||
|
* control, things like lm75 etc... shall match with their
|
||||||
|
* corresponding windfarm drivers, _NOT_ the generic ones,
|
||||||
|
* so we force a prefix of AAPL, onto the modalias to
|
||||||
|
* make that happen
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First try proper modalias */
|
||||||
|
if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) {
|
||||||
|
snprintf(type, type_size, "MAC,%s", tmp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now look for known workarounds */
|
||||||
|
if (!strcmp(node->name, "deq")) {
|
||||||
|
/* Apple uses address 0x34 for TAS3001 and 0x35 for TAS3004 */
|
||||||
|
if (addr == 0x34) {
|
||||||
|
snprintf(type, type_size, "MAC,tas3001");
|
||||||
|
return true;
|
||||||
|
} else if (addr == 0x35) {
|
||||||
|
snprintf(type, type_size, "MAC,tas3004");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(&adap->dev, "i2c-powermac: modalias failure"
|
||||||
|
" on %s\n", node->full_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap,
|
static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap,
|
||||||
struct pmac_i2c_bus *bus)
|
struct pmac_i2c_bus *bus)
|
||||||
{
|
{
|
||||||
struct i2c_client *newdev;
|
struct i2c_client *newdev;
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
|
bool found_onyx = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In some cases we end up with the via-pmu node itself, in this
|
||||||
|
* case we skip this function completely as the device-tree will
|
||||||
|
* not contain anything useful.
|
||||||
|
*/
|
||||||
|
if (!strcmp(adap->dev.of_node->name, "via-pmu"))
|
||||||
|
return;
|
||||||
|
|
||||||
for_each_child_of_node(adap->dev.of_node, node) {
|
for_each_child_of_node(adap->dev.of_node, node) {
|
||||||
struct i2c_board_info info = {};
|
struct i2c_board_info info = {};
|
||||||
struct dev_archdata dev_ad = {};
|
|
||||||
const __be32 *reg;
|
|
||||||
char tmp[16];
|
|
||||||
u32 addr;
|
u32 addr;
|
||||||
int len;
|
|
||||||
|
|
||||||
/* Get address & channel */
|
/* Get address & channel */
|
||||||
reg = of_get_property(node, "reg", &len);
|
addr = i2c_powermac_get_addr(adap, bus, node);
|
||||||
if (!reg || (len < sizeof(int))) {
|
if (addr == 0xffffffff)
|
||||||
dev_err(&adap->dev, "i2c-powermac: invalid reg on %s\n",
|
|
||||||
node->full_name);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
addr = be32_to_cpup(reg);
|
|
||||||
|
|
||||||
/* Multibus setup, check channel */
|
/* Multibus setup, check channel */
|
||||||
if (!pmac_i2c_match_adapter(node, adap))
|
if (!pmac_i2c_match_adapter(node, adap))
|
||||||
|
@ -257,27 +367,23 @@ static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap,
|
||||||
dev_dbg(&adap->dev, "i2c-powermac: register %s\n",
|
dev_dbg(&adap->dev, "i2c-powermac: register %s\n",
|
||||||
node->full_name);
|
node->full_name);
|
||||||
|
|
||||||
/* Make up a modalias. Note: we to _NOT_ want the standard
|
/*
|
||||||
* i2c drivers to match with any of our powermac stuff
|
* Keep track of some device existence to handle
|
||||||
* unless they have been specifically modified to handle
|
* workarounds later.
|
||||||
* it on a case by case basis. For example, for thermal
|
|
||||||
* control, things like lm75 etc... shall match with their
|
|
||||||
* corresponding windfarm drivers, _NOT_ the generic ones,
|
|
||||||
* so we force a prefix of AAPL, onto the modalias to
|
|
||||||
* make that happen
|
|
||||||
*/
|
*/
|
||||||
if (of_modalias_node(node, tmp, sizeof(tmp)) < 0) {
|
if (of_device_is_compatible(node, "pcm3052"))
|
||||||
dev_err(&adap->dev, "i2c-powermac: modalias failure"
|
found_onyx = true;
|
||||||
" on %s\n", node->full_name);
|
|
||||||
|
/* Make up a modalias */
|
||||||
|
if (!i2c_powermac_get_type(adap, node, addr,
|
||||||
|
info.type, sizeof(info.type))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
snprintf(info.type, sizeof(info.type), "MAC,%s", tmp);
|
|
||||||
|
|
||||||
/* Fill out the rest of the info structure */
|
/* Fill out the rest of the info structure */
|
||||||
info.addr = (addr & 0xff) >> 1;
|
info.addr = addr;
|
||||||
info.irq = irq_of_parse_and_map(node, 0);
|
info.irq = irq_of_parse_and_map(node, 0);
|
||||||
info.of_node = of_node_get(node);
|
info.of_node = of_node_get(node);
|
||||||
info.archdata = &dev_ad;
|
|
||||||
|
|
||||||
newdev = i2c_new_device(adap, &info);
|
newdev = i2c_new_device(adap, &info);
|
||||||
if (!newdev) {
|
if (!newdev) {
|
||||||
|
@ -292,6 +398,9 @@ static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Additional workarounds */
|
||||||
|
i2c_powermac_add_missing(adap, bus, found_onyx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit i2c_powermac_probe(struct platform_device *dev)
|
static int __devinit i2c_powermac_probe(struct platform_device *dev)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user