[PATCH V2 45/69] ST SPEAr: PCIE gadget suppport

Andrew Morton akpm at linux-foundation.org
Tue Oct 19 17:47:25 EDT 2010


On Fri,  1 Oct 2010 17:26:05 +0530
Viresh KUMAR <viresh.kumar at st.com> wrote:

> From: Pratyush Anand <pratyush.anand at st.com>
> 
> This is a configurable gadget. can be configured by sysfs interface. Any
> IP available at PCIE bus can be programmed to be used by host
> controller.It supoorts both INTX and MSI.
> By default, gadget is configured for INTX and SYSRAM1 is mapped to BAR0
> with size 0x1000
> 
>
> ...
>
> +static void enable_dbi_access(struct pcie_app_reg *app_reg)

app_reg should have the __iomem tag.

> +{
> +	/* Enable DBI access */
> +	writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
> +			&app_reg->slv_armisc);
> +	writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
> +			&app_reg->slv_awmisc);
> +
> +}
> +
> +static void disable_dbi_access(struct pcie_app_reg *app_reg)

ditto

> +{
> +	/* disable DBI access */
> +	writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
> +			&app_reg->slv_armisc);
> +	writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
> +			&app_reg->slv_awmisc);
> +
> +}
> +
> +static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
> +		int where, int size, u32 *val)
> +{
> +	struct pcie_app_reg *app_reg
> +		= (struct pcie_app_reg *) config->va_app_base;

ditto

> +	u32 va_address;
> +
> +	/* Enable DBI access */
> +	enable_dbi_access(app_reg);
> +
> +	va_address = (u32)config->va_dbi_base + (where & ~0x3);
> +
> +	*val = readl(va_address);
> +
> +	if (size == 1)
> +		*val = (*val >> (8 * (where & 3))) & 0xff;
> +	else if (size == 2)
> +		*val = (*val >> (8 * (where & 3))) & 0xffff;
> +
> +	/* Disable DBI access */
> +	disable_dbi_access(app_reg);
> +}
> +
> +static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
> +		int where, int size, u32 val)
> +{
> +	struct pcie_app_reg *app_reg
> +		= (struct pcie_app_reg *) config->va_app_base;

etc.

> +	u32 va_address;
> +
> +	/* Enable DBI access */
> +	enable_dbi_access(app_reg);
> +
> +	va_address = (u32)config->va_dbi_base + (where & ~0x3);
> +
> +	if (size == 4)
> +		writel(val, va_address);
> +	else if (size == 2)
> +		writew(val, va_address + (where & 2));
> +	else if (size == 1)
> +		writeb(val, va_address + (where & 3));
> +
> +	/* Disable DBI access */
> +	disable_dbi_access(app_reg);
> +}
> +
>
> ...
>
> +/**

This token is specifically used to introduce a kerneldoc comment, but
this wasn't a kerneldoc comment.

> + * Tell if a device supports a given PCI capability.
> + * Returns the address of the requested capability structure within the
> + * device's PCI configuration space or 0 in case the device does not
> + * support it. Possible values for @cap:
> + *
> + * %PCI_CAP_ID_PM	Power Management
> + * %PCI_CAP_ID_AGP	Accelerated Graphics Port
> + * %PCI_CAP_ID_VPD	Vital Product Data
> + * %PCI_CAP_ID_SLOTID	Slot Identification
> + * %PCI_CAP_ID_MSI	Message Signalled Interrupts
> + * %PCI_CAP_ID_CHSWP	CompactPCI HotSwap
> + * %PCI_CAP_ID_PCIX	PCI-X
> + * %PCI_CAP_ID_EXP	PCI Express
> + */
>
> ...
>
> +
> +static ssize_t pcie_gadget_show_link(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	struct pcie_app_reg *app_reg =
> +		(struct pcie_app_reg *)config->va_app_base;

Check all the __iomems;

> +	if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID))
> +		return sprintf(buf, "UP");
> +	else
> +		return sprintf(buf, "DOWN");
> +}
> +
> +static ssize_t pcie_gadget_store_link(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	struct pcie_app_reg *app_reg =
> +		(struct pcie_app_reg *)config->va_app_base;
> +	char link[10];
> +
> +	if (sscanf(buf, "%s", link) != 1)

What happens if strlen(buf) >= 10?

> +		return -EINVAL;
> +
> +	if (!strcmp(link, "UP"))
> +		writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID),
> +			&app_reg->app_ctrl_0);
> +	else
> +		writel(readl(&app_reg->app_ctrl_0)
> +				& ~(1 << APP_LTSSM_ENABLE_ID),
> +				&app_reg->app_ctrl_0);
> +	return count;
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_store_int_type(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	char int_type[10];
> +	u32 cap, vector, vec, flags;
> +
> +	if (sscanf(buf, "%s", int_type) != 1)

ditto

> +		return -EINVAL;
> +
> +	if (!strcmp(int_type, "INTA"))
> +		spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
> +
> +	else if (!strcmp(int_type, "MSI")) {
> +		vector = config->requested_msi;
> +		vec = 0;
> +		while (vector > 1) {
> +			vector /= 2;
> +			vec++;
> +		}
> +		spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0);
> +		cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
> +		spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
> +		flags &= ~PCI_MSI_FLAGS_QMASK;
> +		flags |= vec << 1;
> +		spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags);
> +	}

No checking for unrecognised input?

> +	strcpy(config->int_type, int_type);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(int_type, S_IWUSR | S_IRUGO, pcie_gadget_show_int_type,
> +		pcie_gadget_store_int_type);
> +
> +static ssize_t pcie_gadget_show_no_of_msi(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	struct pcie_app_reg *app_reg =
> +		(struct pcie_app_reg *)config->va_app_base;

__iomem

> +	u32 cap, vector, vec, flags;
> +
> +	if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID))
> +			!= (1 << CFG_MSI_EN_ID))
> +		vector = 0;
> +	else {
> +		cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
> +		spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
> +		flags &= ~PCI_MSI_FLAGS_QSIZE;
> +		vec = flags >> 4;
> +		vector = 1;
> +		while (vec--)
> +			vector *= 2;
> +	}
> +	config->configured_msi = vector;
> +
> +	return sprintf(buf, "%u", vector);
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_store_inta(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	struct pcie_app_reg *app_reg =
> +		(struct pcie_app_reg *)config->va_app_base;
> +	int en;
> +
> +	if (sscanf(buf, "%d", &en) != 1)

strict_strtoul() would be better.  It will reject input of the form "42foo".

> +		return -EINVAL;
> +
> +	if (en)
> +		writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
> +				&app_reg->app_ctrl_0);
> +	else
> +		writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID),
> +				&app_reg->app_ctrl_0);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(inta, S_IWUSR, NULL, pcie_gadget_store_inta);
> +
> +static ssize_t pcie_gadget_store_send_msi(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	struct pcie_app_reg *app_reg =
> +		(struct pcie_app_reg *)config->va_app_base;
> +	int vector;
> +	u32 ven_msi;
> +
> +	if (sscanf(buf, "%d", &vector) != 1)

strict_strtoul().

> +		return -EINVAL;
> +
> +	if (!config->configured_msi)
> +		return -EINVAL;
> +
> +	if (vector >= config->configured_msi)
> +		return -EINVAL;
> +
> +	ven_msi = readl(&app_reg->ven_msi_1);
> +	ven_msi &= ~VEN_MSI_FUN_NUM_MASK;
> +	ven_msi |= 0 << VEN_MSI_FUN_NUM_ID;
> +	ven_msi &= ~VEN_MSI_TC_MASK;
> +	ven_msi |= 0 << VEN_MSI_TC_ID;
> +	ven_msi &= ~VEN_MSI_VECTOR_MASK;
> +	ven_msi |= vector << VEN_MSI_VECTOR_ID;
> +
> +	/*generating interrupt for msi vector*/
> +	ven_msi |= VEN_MSI_REQ_EN;
> +	writel(ven_msi, &app_reg->ven_msi_1);
> +	/*need to wait till this bit is cleared, it is not cleared
> +	 * autometically[Bug RTL] TBD*/
> +	udelay(1);
> +	ven_msi &= ~VEN_MSI_REQ_EN;
> +	writel(ven_msi, &app_reg->ven_msi_1);
> +
> +	return count;
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_store_vendor_id(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	u32 id;
> +
> +	if (sscanf(buf, "%x", &id) != 1)

strict_strtoul can be used here as well?

> +		return -EINVAL;
> +
> +	spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
> +
> +	return count;
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_store_device_id(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	u32 id;
> +
> +	if (sscanf(buf, "%x", &id) != 1)

etc.

> +		return -EINVAL;
> +
> +	spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
> +
> +	return count;
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_store_bar0_size(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	u32 size, pos, pos1;
> +	u32 no_of_bit = 0;
> +
> +	if (sscanf(buf, "%x", &size) != 1)

etc.

> +		return -EINVAL;
> +	/* as per PCIE specs, min bar size supported is 128 bytes. But
> +	 * our controller supports min as 256*/
> +	if (size <= 0x100)
> +		size = 0x100;
> +	/* max bar size is 1MB*/
> +	else if (size >= 0x100000)
> +		size = 0x100000;
> +	else {
> +		pos = 0;
> +		pos1 = 0;
> +		while (pos < 21) {
> +			pos = find_next_bit((unsigned long *)&size, 21, pos);
> +			if (pos != 21)
> +				pos1 = pos + 1;
> +			pos++;
> +			no_of_bit++;
> +		}
> +		if (no_of_bit == 2)
> +			pos1--;
> +
> +		size = 1 << pos1;
> +	}
> +	config->bar0_size = size;
> +	spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1);
> +
> +	return count;
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_show_bar0_address(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct spear_pcie_gadget_config *config = dev_get_drvdata(dev);
> +	struct pcie_app_reg *app_reg =
> +		(struct pcie_app_reg *)config->va_app_base;

etc

> +	u32 address = readl(&app_reg->pim0_mem_addr_start);
> +
> +	return sprintf(buf, "%x", address);
> +}
> +
>
> ...
>
> +static ssize_t pcie_gadget_show_help(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	char text[] = "\t\tlink read->ltssm status\n \
> +		link write->arg1 = UP to enable ltsmm DOWN to disable\n \
> +		int_type read->type of supported interrupt\n \
> +		int_type write->arg1 = interrupt type to be configured and\n \
> +		can be INTA, MSI or NO_INT\n \
> +		(select MSI only when you have programmed no_of_msi)\n \
> +		no_of_msi read->zero if MSI is not enabled by host\n \
> +		and positive value is the number of MSI vector granted\n \
> +		no_of_msi write->arg1 = number of MSI vector needed\n \
> +		inta write->arg1 = 1 to assert INTA and 0 to de-assert\n \
> +		send_msi write->arg1 = MSI vector to be send\n \
> +		vendor_id read->programmed vendor id (hex)\n\
> +		vendor_id write->arg1 = vendor id(hex) to be programmed\n \
> +		device_id read->programmed device id(hex)\n \
> +		device_id write->arg1 = device id(hex) to be programmed\n \
> +		bar0_size read->size of bar0 in hex\n \
> +		bar0_size write->arg1= size of bar0 in hex\n \
> +		(default bar0 size is 1000 (hex) bytes)\n \
> +		bar0_address read->address of bar0 mapped area in hex\n \
> +		bar0_address write->arg1 = address of bar0 mapped area in hex\n\
> +		(default mapping of bar0 is SYSRAM1(E0800000)\n \
> +		(always program bar size before bar address)\n \
> +		(kernel might modify bar size and address to align)\n \
> +		(read back bar size and address after writing to check)\n \
> +		bar0_rw_offset read->offset of bar0 for which bar0_data \n \
> +		will return value\n \
> +		bar0_rw_offset write->arg1 = offset of bar0 for which\n \
> +		bar0_data will write value\n \
> +		bar0_data read->data at bar0_rw_offset\n \
> +		bar0_data write->arg1 = data to be written at\n \
> +		bar0_rw_offset\n";
> +
> +	int size = (sizeof(text) < PAGE_SIZE) ? sizeof(text) : PAGE_SIZE;
> +
> +	return snprintf(buf, size, "%s", text);
> +}

What the heck is this??

> +static DEVICE_ATTR(help, S_IRUGO, pcie_gadget_show_help, NULL);
> +
> +static struct attribute *pcie_gadget_attributes[] = {
> +	&dev_attr_link.attr,
> +	&dev_attr_int_type.attr,
> +	&dev_attr_no_of_msi.attr,
> +	&dev_attr_inta.attr,
> +	&dev_attr_send_msi.attr,
> +	&dev_attr_vendor_id.attr,
> +	&dev_attr_device_id.attr,
> +	&dev_attr_bar0_size.attr,
> +	&dev_attr_bar0_address.attr,
> +	&dev_attr_bar0_rw_offset.attr,
> +	&dev_attr_bar0_data.attr,
> +	&dev_attr_help.attr,
> +	NULL
> +};
> +
>
> ...
>
> +static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
> +{
> +	struct resource *res0, *res1;
> +	struct spear_pcie_gadget_config *config;
> +	unsigned int status = 0;
> +	int irq;
> +	struct clk *clk;
> +
> +	/* get resource for application registers*/
> +
> +	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res0) {
> +		dev_err(&pdev->dev, "no resource defined\n");
> +		return -EBUSY;
> +	}
> +	if (!request_mem_region(res0->start, resource_size(res0),
> +				pdev->name)) {
> +		dev_err(&pdev->dev, "pcie gadget region already	claimed\n");
> +		return -EBUSY;
> +	}
> +	/* get resource for dbi registers*/
> +
> +	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!res1) {
> +		dev_err(&pdev->dev, "no resource defined\n");
> +		goto err_rel_res0;
> +	}
> +	if (!request_mem_region(res1->start, resource_size(res1),
> +				pdev->name)) {
> +		dev_err(&pdev->dev, "pcie gadget region already	claimed\n");
> +		goto err_rel_res0;
> +	}
> +
> +	config = kzalloc(sizeof(*config), GFP_KERNEL);
> +	if (!config) {
> +		dev_err(&pdev->dev, "out of memory\n");
> +		status = -ENOMEM;
> +		goto err_rel_res;
> +	}
> +
> +	config->va_app_base = ioremap(res0->start, resource_size(res0));
> +	if (!config->va_app_base) {
> +		dev_err(&pdev->dev, "ioremap fail\n");
> +		status = -ENOMEM;
> +		goto err_kzalloc;
> +	}
> +
> +	config->base = (void *)res1->start;

Is that __iomem?

> +	config->va_dbi_base = ioremap(res1->start, resource_size(res1));
> +	if (!config->va_dbi_base) {
> +		dev_err(&pdev->dev, "ioremap fail\n");
> +		status = -ENOMEM;
> +		goto err_iounmap_app;
> +	}
> +
> +	dev_set_drvdata(&pdev->dev, config);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no update irq?\n");
> +		status = irq;
> +		goto err_iounmap;
> +	}
> +
> +	status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL);
> +	if (status) {
> +		dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \
> +				claimed\n", irq);
> +		goto err_get_irq;
> +	}
> +	/* Register sysfs hooks */
> +	status = sysfs_create_group(&pdev->dev.kobj, &pcie_gadget_attr_group);
> +	if (status)
> +		goto err_irq;
> +
> +	/* init basic pcie application registers*/
> +	/* do not enable clock if it is PCIE0.Ideally , all controller should
> +	 * have been independent from others with respect to clock. But PCIE1
> +	 * and 2 depends on PCIE0.So PCIE0 clk is provided during board init.*/
> +	if (pdev->id == 1) {
> +		/* Ideally CFG Clock should have been also enabled here. But
> +		 * it is done currently during board init routne*/
> +		clk = clk_get_sys("pcie1", NULL);
> +		if (!clk) {
> +			pr_err("%s:couldn't get clk for pcie1\n", __func__);
> +			goto err_irq;
> +		}
> +		if (clk_enable(clk)) {
> +			pr_err("%s:couldn't enable clk for pcie1\n", __func__);
> +			goto err_irq;
> +		}
> +	} else if (pdev->id == 2) {
> +		/* Ideally CFG Clock should have been also enabled here. But
> +		 * it is done currently during board init routne*/
> +		clk = clk_get_sys("pcie2", NULL);
> +		if (!clk) {
> +			pr_err("%s:couldn't get clk for pcie2\n", __func__);
> +			goto err_irq;
> +		}
> +		if (clk_enable(clk)) {
> +			pr_err("%s:couldn't enable clk for pcie2\n", __func__);
> +			goto err_irq;
> +		}
> +	}
> +	spear13xx_pcie_device_init(config);
> +
> +	return 0;
> +err_irq:
> +	free_irq(irq, NULL);
> +err_get_irq:
> +	dev_set_drvdata(&pdev->dev, NULL);
> +err_iounmap:
> +	iounmap(config->va_dbi_base);
> +err_iounmap_app:
> +	iounmap(config->va_app_base);
> +err_kzalloc:
> +	kfree(config);
> +err_rel_res:
> +	release_mem_region(res1->start, resource_size(res1));
> +err_rel_res0:
> +	release_mem_region(res0->start, resource_size(res0));
> +	return status;
> +}
> +
>
> ...
>

The driver implements a large, complex userspace interface and afaict
that interface wasn't documented anywhere. 

But the interface is the most important part of the driver!  It should
be documented in a permanent fashion so that reviewers can understand
and review your proposed interface.  They will want to do that before
even looking at the code.



More information about the linux-arm-kernel mailing list