All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wolfram Sang <wsa@the-dreams.de>
To: Tony Prisk <linux@prisktech.co.nz>
Cc: linux-arm-kernel@lists.infradead.org,
	vt8500-wm8505-linux-kernel@googlegroups.com,
	linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org
Subject: Re: [PATCH] i2c: vt8500: Add support for I2C bus on Wondermedia SoCs
Date: Mon, 10 Jun 2013 18:03:29 +0200	[thread overview]
Message-ID: <20130610160328.GG2987@katana> (raw)
In-Reply-To: <1368350634-12654-1-git-send-email-linux@prisktech.co.nz>

[-- Attachment #1: Type: text/plain, Size: 7621 bytes --]

Hi Tony,

> +static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
> diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
> new file mode 100644
> index 0000000..5cebb29
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-wmt.c

...

> +{
> +	u16 val;
> +	int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < 100000; i++) {
> +		val = readw(i2c_dev->base + REG_CSR);
> +		if (val & CSR_READY_MASK)
> +			break;
> +
> +		udelay(1);
> +	}
> +	if (i >= 9999999)
> +		ret = -EBUSY;

What about using time_after and usleep_range?

> +
> +	return ret;
> +}
> +static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> +						   int restart, int last)
> +{
> +	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
> +	u16 val, tcr_val;
> +	int ret, wait_result;
> +	int xfer_len = 0;
> +
> +	if (!restart) {
> +		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pmsg->len == 0) {
> +		/*
> +		 * We still need to run through the while (..) once, so
> +		 * start at -1 and break out early from the loop
> +		 */
> +		xfer_len = -1;
> +		writew(0, i2c_dev->base + REG_CDR);
> +	} else {
> +		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
> +	}
> +
> +	if (!restart) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val &= ~CR_TX_END;
> +		writew(val, i2c_dev->base + REG_CR);
> +
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_CPU_RDY;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	init_completion(&i2c_dev->complete);

INIT_COMPLETION (and init_completion in probe)

> +static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> +						  int restart, int last)
> +{
> +	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
> +	u16 val, tcr_val;
> +	int ret, wait_result;
> +	u32 xfer_len = 0;
> +
> +	if (!restart) {
> +		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	val = readw(i2c_dev->base + REG_CR);
> +	val &= ~CR_TX_END;
> +	writew(val, i2c_dev->base + REG_CR);
> +
> +	val = readw(i2c_dev->base + REG_CR);
> +	val &= ~CR_TX_NEXT_NO_ACK;
> +	writew(val, i2c_dev->base + REG_CR);
> +
> +	if (!restart) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_CPU_RDY;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	if (pmsg->len == 1) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_TX_NEXT_NO_ACK;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	init_completion(&i2c_dev->complete);

ditto

> +static int wmt_i2c_xfer(struct i2c_adapter *adap,
> +			struct i2c_msg msgs[],
> +			int num)
> +{
> +	struct i2c_msg *pmsg;
> +	int i, is_last, restart;
> +	int ret = 0;
> +
> +	for (i = 0; ret >= 0 && i < num; i++) {
> +		is_last = ((i + 1) == num);
> +		restart = (i != 0);
> +
> +		pmsg = &msgs[i];
> +		if (pmsg->flags & I2C_M_NOSTART)
> +			restart = 1;

NOSTART is not restart!? Either your variable is misnamed or check
Documentation/i2c/i2c-protocol what it is about.

> +		if (pmsg->flags & I2C_M_RD)
> +			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
> +		else
> +			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
> +	}
> +
> +	return (ret < 0) ? ret : i;
> +}
> +
> +static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
> +{
> +	int err;
> +
> +	err = clk_prepare_enable(i2c_dev->clk);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to enable clock\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(i2c_dev->clk, 20000000);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
> +		return err;
> +	}
> +
> +	writew(0, i2c_dev->base + REG_CR);
> +	writew(12, i2c_dev->base + REG_MCR);

What are those magic values?

> +	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
> +	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
> +	writew(CR_ENABLE, i2c_dev->base + REG_CR);
> +	readw(i2c_dev->base + REG_CSR);		/* read clear */
> +	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
> +
> +	if (i2c_dev->mode == I2C_MODE_STANDARD)
> +		writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR);
> +	else
> +		writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR);
> +
> +	return 0;
> +}
> +
> +static int wmt_i2c_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct wmt_i2c_dev *i2c_dev;
> +	struct i2c_adapter *adap;
> +	struct resource *res;
> +	int err;
> +	u32 clk_rate;
> +
> +	if (!np) {
> +		dev_err(&pdev->dev, "device node not found\n");
> +		return -ENODEV;
> +	}

This can't happen since we were matched by the driver core.

> +
> +	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
> +	if (!i2c_dev) {
> +		dev_err(&pdev->dev, "device memory allocation failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(i2c_dev->base))
> +		return PTR_ERR(i2c_dev->base);
> +
> +	i2c_dev->irq = irq_of_parse_and_map(np, 0);
> +	if (!i2c_dev->irq) {
> +		dev_err(&pdev->dev, "irq missing or invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	i2c_dev->clk = of_clk_get(np, 0);
> +	if (IS_ERR(i2c_dev->clk)) {
> +		dev_err(&pdev->dev, "unable to request clock\n");
> +		return PTR_ERR(i2c_dev->clk);
> +	}
> +
> +	i2c_dev->mode = I2C_MODE_STANDARD;
> +	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
> +	if ((!err) && (clk_rate == 400000))
> +		i2c_dev->mode = I2C_MODE_FAST;
> +
> +	i2c_dev->dev = &pdev->dev;
> +
> +	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
> +							"i2c", i2c_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
> +		return err;
> +	}
> +
> +	adap = &i2c_dev->adapter;
> +	i2c_set_adapdata(adap, i2c_dev);
> +	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
> +	adap->owner = THIS_MODULE;
> +	adap->class = I2C_CLASS_HWMON;

Don't use .class unless you have an explicit reason to do so. It may
cost boot-time.

> +	adap->algo = &wmt_i2c_algo;
> +	adap->dev.parent = &pdev->dev;
> +	adap->dev.of_node = pdev->dev.of_node;
> +	adap->nr = of_alias_get_id(pdev->dev.of_node, "i2c");
> +
> +	err = wmt_i2c_reset_hardware(i2c_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "error initializing hardware\n");
> +		return err;
> +	}
> +
> +	if (adap->nr < 0)
> +		err = i2c_add_adapter(adap);
> +	else
> +		err = i2c_add_numbered_adapter(adap);

add_numbered_adapter does handle this case internally.

> +
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to add adapter\n");
> +		return err;
> +	}
> +
> +	platform_set_drvdata(pdev, i2c_dev);
> +
> +	of_i2c_register_devices(adap);
> +
> +	return 0;
> +}
> +
> +static int wmt_i2c_remove(struct platform_device *pdev)
> +{
> +	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
> +
> +	/* Disable interrupts, clock and delete adapter */
> +	writew(0, i2c_dev->base + REG_IMR);
> +	clk_disable_unprepare(i2c_dev->clk);
> +	i2c_del_adapter(&i2c_dev->adapter);
> +
> +	return 0;
> +}
> +
> +static struct of_device_id wmt_i2c_dt_ids[] = {
> +	{ .compatible = "wm,wm8505-i2c" },
> +	{ /* Sentinel */ },
> +};
> +
> +static struct platform_driver wmt_i2c_driver = {
> +	.probe		= wmt_i2c_probe,
> +	.remove		= wmt_i2c_remove,
> +	.driver		= {
> +		.name	= "wmt-i2c",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = wmt_i2c_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(wmt_i2c_driver);
> +
> +MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
> +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
> +MODULE_LICENSE("GPL v2");

Header says GPLv2+

> +MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);

Thanks,

   Wolfram


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

WARNING: multiple messages have this Message-ID (diff)
From: Wolfram Sang <wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org>
To: Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	vt8500-wm8505-linux-kernel-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [PATCH] i2c: vt8500: Add support for I2C bus on Wondermedia SoCs
Date: Mon, 10 Jun 2013 18:03:29 +0200	[thread overview]
Message-ID: <20130610160328.GG2987@katana> (raw)
In-Reply-To: <1368350634-12654-1-git-send-email-linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 7645 bytes --]

Hi Tony,

> +static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
> diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
> new file mode 100644
> index 0000000..5cebb29
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-wmt.c

...

> +{
> +	u16 val;
> +	int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < 100000; i++) {
> +		val = readw(i2c_dev->base + REG_CSR);
> +		if (val & CSR_READY_MASK)
> +			break;
> +
> +		udelay(1);
> +	}
> +	if (i >= 9999999)
> +		ret = -EBUSY;

What about using time_after and usleep_range?

> +
> +	return ret;
> +}
> +static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> +						   int restart, int last)
> +{
> +	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
> +	u16 val, tcr_val;
> +	int ret, wait_result;
> +	int xfer_len = 0;
> +
> +	if (!restart) {
> +		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pmsg->len == 0) {
> +		/*
> +		 * We still need to run through the while (..) once, so
> +		 * start at -1 and break out early from the loop
> +		 */
> +		xfer_len = -1;
> +		writew(0, i2c_dev->base + REG_CDR);
> +	} else {
> +		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
> +	}
> +
> +	if (!restart) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val &= ~CR_TX_END;
> +		writew(val, i2c_dev->base + REG_CR);
> +
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_CPU_RDY;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	init_completion(&i2c_dev->complete);

INIT_COMPLETION (and init_completion in probe)

> +static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> +						  int restart, int last)
> +{
> +	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
> +	u16 val, tcr_val;
> +	int ret, wait_result;
> +	u32 xfer_len = 0;
> +
> +	if (!restart) {
> +		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	val = readw(i2c_dev->base + REG_CR);
> +	val &= ~CR_TX_END;
> +	writew(val, i2c_dev->base + REG_CR);
> +
> +	val = readw(i2c_dev->base + REG_CR);
> +	val &= ~CR_TX_NEXT_NO_ACK;
> +	writew(val, i2c_dev->base + REG_CR);
> +
> +	if (!restart) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_CPU_RDY;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	if (pmsg->len == 1) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_TX_NEXT_NO_ACK;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	init_completion(&i2c_dev->complete);

ditto

> +static int wmt_i2c_xfer(struct i2c_adapter *adap,
> +			struct i2c_msg msgs[],
> +			int num)
> +{
> +	struct i2c_msg *pmsg;
> +	int i, is_last, restart;
> +	int ret = 0;
> +
> +	for (i = 0; ret >= 0 && i < num; i++) {
> +		is_last = ((i + 1) == num);
> +		restart = (i != 0);
> +
> +		pmsg = &msgs[i];
> +		if (pmsg->flags & I2C_M_NOSTART)
> +			restart = 1;

NOSTART is not restart!? Either your variable is misnamed or check
Documentation/i2c/i2c-protocol what it is about.

> +		if (pmsg->flags & I2C_M_RD)
> +			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
> +		else
> +			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
> +	}
> +
> +	return (ret < 0) ? ret : i;
> +}
> +
> +static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
> +{
> +	int err;
> +
> +	err = clk_prepare_enable(i2c_dev->clk);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to enable clock\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(i2c_dev->clk, 20000000);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
> +		return err;
> +	}
> +
> +	writew(0, i2c_dev->base + REG_CR);
> +	writew(12, i2c_dev->base + REG_MCR);

What are those magic values?

> +	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
> +	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
> +	writew(CR_ENABLE, i2c_dev->base + REG_CR);
> +	readw(i2c_dev->base + REG_CSR);		/* read clear */
> +	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
> +
> +	if (i2c_dev->mode == I2C_MODE_STANDARD)
> +		writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR);
> +	else
> +		writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR);
> +
> +	return 0;
> +}
> +
> +static int wmt_i2c_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct wmt_i2c_dev *i2c_dev;
> +	struct i2c_adapter *adap;
> +	struct resource *res;
> +	int err;
> +	u32 clk_rate;
> +
> +	if (!np) {
> +		dev_err(&pdev->dev, "device node not found\n");
> +		return -ENODEV;
> +	}

This can't happen since we were matched by the driver core.

> +
> +	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
> +	if (!i2c_dev) {
> +		dev_err(&pdev->dev, "device memory allocation failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(i2c_dev->base))
> +		return PTR_ERR(i2c_dev->base);
> +
> +	i2c_dev->irq = irq_of_parse_and_map(np, 0);
> +	if (!i2c_dev->irq) {
> +		dev_err(&pdev->dev, "irq missing or invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	i2c_dev->clk = of_clk_get(np, 0);
> +	if (IS_ERR(i2c_dev->clk)) {
> +		dev_err(&pdev->dev, "unable to request clock\n");
> +		return PTR_ERR(i2c_dev->clk);
> +	}
> +
> +	i2c_dev->mode = I2C_MODE_STANDARD;
> +	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
> +	if ((!err) && (clk_rate == 400000))
> +		i2c_dev->mode = I2C_MODE_FAST;
> +
> +	i2c_dev->dev = &pdev->dev;
> +
> +	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
> +							"i2c", i2c_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
> +		return err;
> +	}
> +
> +	adap = &i2c_dev->adapter;
> +	i2c_set_adapdata(adap, i2c_dev);
> +	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
> +	adap->owner = THIS_MODULE;
> +	adap->class = I2C_CLASS_HWMON;

Don't use .class unless you have an explicit reason to do so. It may
cost boot-time.

> +	adap->algo = &wmt_i2c_algo;
> +	adap->dev.parent = &pdev->dev;
> +	adap->dev.of_node = pdev->dev.of_node;
> +	adap->nr = of_alias_get_id(pdev->dev.of_node, "i2c");
> +
> +	err = wmt_i2c_reset_hardware(i2c_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "error initializing hardware\n");
> +		return err;
> +	}
> +
> +	if (adap->nr < 0)
> +		err = i2c_add_adapter(adap);
> +	else
> +		err = i2c_add_numbered_adapter(adap);

add_numbered_adapter does handle this case internally.

> +
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to add adapter\n");
> +		return err;
> +	}
> +
> +	platform_set_drvdata(pdev, i2c_dev);
> +
> +	of_i2c_register_devices(adap);
> +
> +	return 0;
> +}
> +
> +static int wmt_i2c_remove(struct platform_device *pdev)
> +{
> +	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
> +
> +	/* Disable interrupts, clock and delete adapter */
> +	writew(0, i2c_dev->base + REG_IMR);
> +	clk_disable_unprepare(i2c_dev->clk);
> +	i2c_del_adapter(&i2c_dev->adapter);
> +
> +	return 0;
> +}
> +
> +static struct of_device_id wmt_i2c_dt_ids[] = {
> +	{ .compatible = "wm,wm8505-i2c" },
> +	{ /* Sentinel */ },
> +};
> +
> +static struct platform_driver wmt_i2c_driver = {
> +	.probe		= wmt_i2c_probe,
> +	.remove		= wmt_i2c_remove,
> +	.driver		= {
> +		.name	= "wmt-i2c",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = wmt_i2c_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(wmt_i2c_driver);
> +
> +MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
> +MODULE_AUTHOR("Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");

Header says GPLv2+

> +MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);

Thanks,

   Wolfram


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

WARNING: multiple messages have this Message-ID (diff)
From: wsa@the-dreams.de (Wolfram Sang)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] i2c: vt8500: Add support for I2C bus on Wondermedia SoCs
Date: Mon, 10 Jun 2013 18:03:29 +0200	[thread overview]
Message-ID: <20130610160328.GG2987@katana> (raw)
In-Reply-To: <1368350634-12654-1-git-send-email-linux@prisktech.co.nz>

Hi Tony,

> +static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
> diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
> new file mode 100644
> index 0000000..5cebb29
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-wmt.c

...

> +{
> +	u16 val;
> +	int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < 100000; i++) {
> +		val = readw(i2c_dev->base + REG_CSR);
> +		if (val & CSR_READY_MASK)
> +			break;
> +
> +		udelay(1);
> +	}
> +	if (i >= 9999999)
> +		ret = -EBUSY;

What about using time_after and usleep_range?

> +
> +	return ret;
> +}
> +static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> +						   int restart, int last)
> +{
> +	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
> +	u16 val, tcr_val;
> +	int ret, wait_result;
> +	int xfer_len = 0;
> +
> +	if (!restart) {
> +		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pmsg->len == 0) {
> +		/*
> +		 * We still need to run through the while (..) once, so
> +		 * start at -1 and break out early from the loop
> +		 */
> +		xfer_len = -1;
> +		writew(0, i2c_dev->base + REG_CDR);
> +	} else {
> +		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
> +	}
> +
> +	if (!restart) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val &= ~CR_TX_END;
> +		writew(val, i2c_dev->base + REG_CR);
> +
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_CPU_RDY;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	init_completion(&i2c_dev->complete);

INIT_COMPLETION (and init_completion in probe)

> +static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> +						  int restart, int last)
> +{
> +	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
> +	u16 val, tcr_val;
> +	int ret, wait_result;
> +	u32 xfer_len = 0;
> +
> +	if (!restart) {
> +		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	val = readw(i2c_dev->base + REG_CR);
> +	val &= ~CR_TX_END;
> +	writew(val, i2c_dev->base + REG_CR);
> +
> +	val = readw(i2c_dev->base + REG_CR);
> +	val &= ~CR_TX_NEXT_NO_ACK;
> +	writew(val, i2c_dev->base + REG_CR);
> +
> +	if (!restart) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_CPU_RDY;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	if (pmsg->len == 1) {
> +		val = readw(i2c_dev->base + REG_CR);
> +		val |= CR_TX_NEXT_NO_ACK;
> +		writew(val, i2c_dev->base + REG_CR);
> +	}
> +
> +	init_completion(&i2c_dev->complete);

ditto

> +static int wmt_i2c_xfer(struct i2c_adapter *adap,
> +			struct i2c_msg msgs[],
> +			int num)
> +{
> +	struct i2c_msg *pmsg;
> +	int i, is_last, restart;
> +	int ret = 0;
> +
> +	for (i = 0; ret >= 0 && i < num; i++) {
> +		is_last = ((i + 1) == num);
> +		restart = (i != 0);
> +
> +		pmsg = &msgs[i];
> +		if (pmsg->flags & I2C_M_NOSTART)
> +			restart = 1;

NOSTART is not restart!? Either your variable is misnamed or check
Documentation/i2c/i2c-protocol what it is about.

> +		if (pmsg->flags & I2C_M_RD)
> +			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
> +		else
> +			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
> +	}
> +
> +	return (ret < 0) ? ret : i;
> +}
> +
> +static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
> +{
> +	int err;
> +
> +	err = clk_prepare_enable(i2c_dev->clk);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to enable clock\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(i2c_dev->clk, 20000000);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
> +		return err;
> +	}
> +
> +	writew(0, i2c_dev->base + REG_CR);
> +	writew(12, i2c_dev->base + REG_MCR);

What are those magic values?

> +	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
> +	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
> +	writew(CR_ENABLE, i2c_dev->base + REG_CR);
> +	readw(i2c_dev->base + REG_CSR);		/* read clear */
> +	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
> +
> +	if (i2c_dev->mode == I2C_MODE_STANDARD)
> +		writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR);
> +	else
> +		writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR);
> +
> +	return 0;
> +}
> +
> +static int wmt_i2c_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct wmt_i2c_dev *i2c_dev;
> +	struct i2c_adapter *adap;
> +	struct resource *res;
> +	int err;
> +	u32 clk_rate;
> +
> +	if (!np) {
> +		dev_err(&pdev->dev, "device node not found\n");
> +		return -ENODEV;
> +	}

This can't happen since we were matched by the driver core.

> +
> +	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
> +	if (!i2c_dev) {
> +		dev_err(&pdev->dev, "device memory allocation failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(i2c_dev->base))
> +		return PTR_ERR(i2c_dev->base);
> +
> +	i2c_dev->irq = irq_of_parse_and_map(np, 0);
> +	if (!i2c_dev->irq) {
> +		dev_err(&pdev->dev, "irq missing or invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	i2c_dev->clk = of_clk_get(np, 0);
> +	if (IS_ERR(i2c_dev->clk)) {
> +		dev_err(&pdev->dev, "unable to request clock\n");
> +		return PTR_ERR(i2c_dev->clk);
> +	}
> +
> +	i2c_dev->mode = I2C_MODE_STANDARD;
> +	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
> +	if ((!err) && (clk_rate == 400000))
> +		i2c_dev->mode = I2C_MODE_FAST;
> +
> +	i2c_dev->dev = &pdev->dev;
> +
> +	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
> +							"i2c", i2c_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
> +		return err;
> +	}
> +
> +	adap = &i2c_dev->adapter;
> +	i2c_set_adapdata(adap, i2c_dev);
> +	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
> +	adap->owner = THIS_MODULE;
> +	adap->class = I2C_CLASS_HWMON;

Don't use .class unless you have an explicit reason to do so. It may
cost boot-time.

> +	adap->algo = &wmt_i2c_algo;
> +	adap->dev.parent = &pdev->dev;
> +	adap->dev.of_node = pdev->dev.of_node;
> +	adap->nr = of_alias_get_id(pdev->dev.of_node, "i2c");
> +
> +	err = wmt_i2c_reset_hardware(i2c_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "error initializing hardware\n");
> +		return err;
> +	}
> +
> +	if (adap->nr < 0)
> +		err = i2c_add_adapter(adap);
> +	else
> +		err = i2c_add_numbered_adapter(adap);

add_numbered_adapter does handle this case internally.

> +
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to add adapter\n");
> +		return err;
> +	}
> +
> +	platform_set_drvdata(pdev, i2c_dev);
> +
> +	of_i2c_register_devices(adap);
> +
> +	return 0;
> +}
> +
> +static int wmt_i2c_remove(struct platform_device *pdev)
> +{
> +	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
> +
> +	/* Disable interrupts, clock and delete adapter */
> +	writew(0, i2c_dev->base + REG_IMR);
> +	clk_disable_unprepare(i2c_dev->clk);
> +	i2c_del_adapter(&i2c_dev->adapter);
> +
> +	return 0;
> +}
> +
> +static struct of_device_id wmt_i2c_dt_ids[] = {
> +	{ .compatible = "wm,wm8505-i2c" },
> +	{ /* Sentinel */ },
> +};
> +
> +static struct platform_driver wmt_i2c_driver = {
> +	.probe		= wmt_i2c_probe,
> +	.remove		= wmt_i2c_remove,
> +	.driver		= {
> +		.name	= "wmt-i2c",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = wmt_i2c_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(wmt_i2c_driver);
> +
> +MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
> +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
> +MODULE_LICENSE("GPL v2");

Header says GPLv2+

> +MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);

Thanks,

   Wolfram

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130610/0cdac200/attachment.sig>

  parent reply	other threads:[~2013-06-10 16:01 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-12  9:23 [PATCH] i2c: vt8500: Add support for I2C bus on Wondermedia SoCs Tony Prisk
2013-05-12  9:23 ` Tony Prisk
2013-05-12  9:23 ` Tony Prisk
2013-05-21  6:16 ` Tony Prisk
2013-05-21  6:16   ` Tony Prisk
2013-06-10 16:03 ` Wolfram Sang [this message]
2013-06-10 16:03   ` Wolfram Sang
2013-06-10 16:03   ` Wolfram Sang
2013-06-10 23:31   ` Russell King - ARM Linux
2013-06-10 23:31     ` Russell King - ARM Linux

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20130610160328.GG2987@katana \
    --to=wsa@the-dreams.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@prisktech.co.nz \
    --cc=vt8500-wm8505-linux-kernel@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.