in busses/i2c-qcom-cci.c [523:668]
static int cci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
unsigned long cci_clk_rate = 0;
struct device_node *child;
struct resource *r;
struct cci *cci;
int ret, i;
u32 val;
cci = devm_kzalloc(dev, sizeof(*cci), GFP_KERNEL);
if (!cci)
return -ENOMEM;
cci->dev = dev;
platform_set_drvdata(pdev, cci);
cci->data = device_get_match_data(dev);
if (!cci->data)
return -ENOENT;
for_each_available_child_of_node(dev->of_node, child) {
u32 idx;
ret = of_property_read_u32(child, "reg", &idx);
if (ret) {
dev_err(dev, "%pOF invalid 'reg' property", child);
continue;
}
if (idx >= cci->data->num_masters) {
dev_err(dev, "%pOF invalid 'reg' value: %u (max is %u)",
child, idx, cci->data->num_masters - 1);
continue;
}
cci->master[idx].adap.quirks = &cci->data->quirks;
cci->master[idx].adap.algo = &cci_algo;
cci->master[idx].adap.dev.parent = dev;
cci->master[idx].adap.dev.of_node = child;
cci->master[idx].master = idx;
cci->master[idx].cci = cci;
i2c_set_adapdata(&cci->master[idx].adap, &cci->master[idx]);
snprintf(cci->master[idx].adap.name,
sizeof(cci->master[idx].adap.name), "Qualcomm-CCI");
cci->master[idx].mode = I2C_MODE_STANDARD;
ret = of_property_read_u32(child, "clock-frequency", &val);
if (!ret) {
if (val == I2C_MAX_FAST_MODE_FREQ)
cci->master[idx].mode = I2C_MODE_FAST;
else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ)
cci->master[idx].mode = I2C_MODE_FAST_PLUS;
}
init_completion(&cci->master[idx].irq_complete);
}
/* Memory */
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cci->base = devm_ioremap_resource(dev, r);
if (IS_ERR(cci->base))
return PTR_ERR(cci->base);
/* Clocks */
ret = devm_clk_bulk_get_all(dev, &cci->clocks);
if (ret < 1) {
dev_err(dev, "failed to get clocks %d\n", ret);
return ret;
}
cci->nclocks = ret;
/* Retrieve CCI clock rate */
for (i = 0; i < cci->nclocks; i++) {
if (!strcmp(cci->clocks[i].id, "cci")) {
cci_clk_rate = clk_get_rate(cci->clocks[i].clk);
break;
}
}
if (cci_clk_rate != cci->data->cci_clk_rate) {
/* cci clock set by the bootloader or via assigned clock rate
* in DT.
*/
dev_warn(dev, "Found %lu cci clk rate while %lu was expected\n",
cci_clk_rate, cci->data->cci_clk_rate);
}
ret = cci_enable_clocks(cci);
if (ret < 0)
return ret;
/* Interrupt */
ret = platform_get_irq(pdev, 0);
if (ret < 0)
goto disable_clocks;
cci->irq = ret;
ret = devm_request_irq(dev, cci->irq, cci_isr, 0, dev_name(dev), cci);
if (ret < 0) {
dev_err(dev, "request_irq failed, ret: %d\n", ret);
goto disable_clocks;
}
val = readl(cci->base + CCI_HW_VERSION);
dev_dbg(dev, "CCI HW version = 0x%08x", val);
ret = cci_reset(cci);
if (ret < 0)
goto error;
ret = cci_init(cci);
if (ret < 0)
goto error;
for (i = 0; i < cci->data->num_masters; i++) {
if (!cci->master[i].cci)
continue;
ret = i2c_add_adapter(&cci->master[i].adap);
if (ret < 0)
goto error_i2c;
}
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
error_i2c:
for (; i >= 0; i--) {
if (cci->master[i].cci)
i2c_del_adapter(&cci->master[i].adap);
}
error:
disable_irq(cci->irq);
disable_clocks:
cci_disable_clocks(cci);
return ret;
}