in busses/i2c-ocores.c [602:741]
static int ocores_i2c_probe(struct platform_device *pdev)
{
struct ocores_i2c *i2c;
struct ocores_i2c_platform_data *pdata;
struct resource *res;
int irq;
int ret;
int i;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
spin_lock_init(&i2c->process_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
i2c->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
} else {
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res)
return -EINVAL;
i2c->iobase = res->start;
if (!devm_request_region(&pdev->dev, res->start,
resource_size(res),
pdev->name)) {
dev_err(&pdev->dev, "Can't get I/O resource.\n");
return -EBUSY;
}
i2c->setreg = oc_setreg_io_8;
i2c->getreg = oc_getreg_io_8;
}
pdata = dev_get_platdata(&pdev->dev);
if (pdata) {
i2c->reg_shift = pdata->reg_shift;
i2c->reg_io_width = pdata->reg_io_width;
i2c->ip_clock_khz = pdata->clock_khz;
if (pdata->bus_khz)
i2c->bus_clock_khz = pdata->bus_khz;
else
i2c->bus_clock_khz = 100;
} else {
ret = ocores_i2c_of_probe(pdev, i2c);
if (ret)
return ret;
}
if (i2c->reg_io_width == 0)
i2c->reg_io_width = 1; /* Set to default value */
if (!i2c->setreg || !i2c->getreg) {
bool be = pdata ? pdata->big_endian :
of_device_is_big_endian(pdev->dev.of_node);
switch (i2c->reg_io_width) {
case 1:
i2c->setreg = oc_setreg_8;
i2c->getreg = oc_getreg_8;
break;
case 2:
i2c->setreg = be ? oc_setreg_16be : oc_setreg_16;
i2c->getreg = be ? oc_getreg_16be : oc_getreg_16;
break;
case 4:
i2c->setreg = be ? oc_setreg_32be : oc_setreg_32;
i2c->getreg = be ? oc_getreg_32be : oc_getreg_32;
break;
default:
dev_err(&pdev->dev, "Unsupported I/O width (%d)\n",
i2c->reg_io_width);
ret = -EINVAL;
goto err_clk;
}
}
init_waitqueue_head(&i2c->wait);
irq = platform_get_irq_optional(pdev, 0);
/*
* Since the SoC does have an interrupt, its DT has an interrupt
* property - But this should be bypassed as the IRQ logic in this
* SoC is broken.
*/
if (of_device_is_compatible(pdev->dev.of_node,
"sifive,fu540-c000-i2c")) {
i2c->flags |= OCORES_FLAG_BROKEN_IRQ;
irq = -ENXIO;
}
if (irq == -ENXIO) {
ocores_algorithm.master_xfer = ocores_xfer_polling;
} else {
if (irq < 0)
return irq;
}
if (ocores_algorithm.master_xfer != ocores_xfer_polling) {
ret = devm_request_any_context_irq(&pdev->dev, irq,
ocores_isr, 0,
pdev->name, i2c);
if (ret) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_clk;
}
}
ret = ocores_init(&pdev->dev, i2c);
if (ret)
goto err_clk;
/* hook up driver to tree */
platform_set_drvdata(pdev, i2c);
i2c->adap = ocores_adapter;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
/* add i2c adapter to i2c tree */
ret = i2c_add_adapter(&i2c->adap);
if (ret)
goto err_clk;
/* add in known devices to the bus */
if (pdata) {
for (i = 0; i < pdata->num_devices; i++)
i2c_new_client_device(&i2c->adap, pdata->devices + i);
}
return 0;
err_clk:
clk_disable_unprepare(i2c->clk);
return ret;
}