ahci_elphel.c 7.16 KB
Newer Older
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Elphel AHCI SATA platform driver for elphel393 camera
 *
 * Based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 */

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ahci_platform.h>
#include <linux/of.h>
#include <linux/of_device.h>
22
#include <linux/of_address.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
23 24 25 26
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include "ahci.h"
Mikhail Karpenko's avatar
Mikhail Karpenko committed
27
#include "libahci_debug.h"
Mikhail Karpenko's avatar
Mikhail Karpenko committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

#define DRV_NAME "elphel-ahci"

/* Property names from device tree, these are specific for the controller */
#define PROP_NAME_CLB_OFFS "clb_offs"
#define PROP_NAME_FB_OFFS "fb_offs"

static struct ata_port_operations ahci_elphel_ops;
static const struct ata_port_info ahci_elphel_port_info;
static struct scsi_host_template ahci_platform_sht;
static const struct of_device_id ahci_elphel_of_match[];

struct elphel_ahci_priv {
	u32 clb_offs;
	u32 fb_offs;
43
	u32 base_addr;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
44 45 46 47 48 49 50 51 52 53 54
};

static int elphel_port_start(struct ata_port *ap)
{
	void *mem;
	dma_addr_t mem_dma;
	struct device *dev = ap->host->dev;
	struct ahci_port_priv *pp;
	struct ahci_host_priv *hpriv = ap->host->private_data;
	const struct elphel_ahci_priv *dpriv = hpriv->plat_data;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
55 56
	libahci_debug_init(ap->host);

Mikhail Karpenko's avatar
Mikhail Karpenko committed
57
	dev_info(dev, "starting port %d", ap->port_no);
58 59
	libahci_debug_wait_flag();

Mikhail Karpenko's avatar
Mikhail Karpenko committed
60
	pp = devm_kzalloc(dev, sizeof(struct ahci_port_priv), GFP_KERNEL);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
61 62 63 64 65 66 67 68
	if (!pp)
		return -ENOMEM;

	mem = dmam_alloc_coherent(dev, AHCI_CMD_TBL_AR_SZ, &mem_dma, GFP_KERNEL);
	if (!mem)
		return -ENOMEM;
	memset(mem, 0, AHCI_CMD_TBL_AR_SZ);
	pp->cmd_tbl = mem;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
69
	pp->cmd_tbl_dma = mem_dma;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
70 71 72 73

	/*
	 * Set predefined addresses
	 */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
74
	pp->cmd_slot = hpriv->mmio + dpriv->clb_offs;
75 76
	//pp->cmd_slot_dma = virt_to_phys(pp->cmd_slot);
	pp->cmd_slot_dma = 0x80000000 + dpriv->clb_offs;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
77 78

	pp->rx_fis = hpriv->mmio + dpriv->fb_offs;
79 80
	//pp->rx_fis_dma = virt_to_phys(pp->rx_fis);
	pp->rx_fis_dma = 0x80000000 + dpriv->fb_offs;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
81

82 83 84 85 86
	/*dev_info(dev, "cmd_slot: 0x%p", pp->cmd_slot);
	dev_info(dev, "cmd_slot_dma: 0x%08u", pp->cmd_slot_dma);
	dev_info(dev, "rx_fis: 0x%p", pp->rx_fis);
	dev_info(dev, "rx_fis_dma: 0x%08u", pp->rx_fis_dma);
	dev_info(dev, "base_addr: 0x%08u", dpriv->base_addr);*/
Mikhail Karpenko's avatar
Mikhail Karpenko committed
87 88 89 90 91

	/*
	 * Save off initial list of interrupts to be enabled.
	 * This could be changed later
	 */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
92
	pp->intr_mask = DEF_PORT_IRQ;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
93 94 95

	ap->private_data = pp;

Andrey Filippov's avatar
Andrey Filippov committed
96 97 98 99 100 101
//	libahci_debug_state_dump(ap);
//	libahci_debug_state_dump(ap);

	libahci_debug_saxigp1_save(ap, 0x3000);
	libahci_debug_saxigp1_save(ap, 0x3000);

102

Mikhail Karpenko's avatar
Mikhail Karpenko committed
103
	return ahci_port_resume(ap);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
104 105 106 107 108 109
}

static int elphel_parse_prop(const struct device_node *devn,
		struct device *dev,
		struct elphel_ahci_priv *dpriv)
{
110 111
	u64 size;
	unsigned int flags;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
112
	const __be32 *val;
113
	struct resource res;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
114 115 116 117 118 119 120 121 122 123

	if (!devn) {
		dev_err(dev, "device tree node is not found");
		return -EINVAL;
	}

	val = of_get_property(devn, PROP_NAME_CLB_OFFS, NULL);
	dpriv->clb_offs = be32_to_cpup(val);
	val = of_get_property(devn, PROP_NAME_FB_OFFS, NULL);
	dpriv->fb_offs = be32_to_cpup(val);
124 125 126 127 128 129 130 131
	val = of_get_address(devn, 0, NULL, NULL);
	if (val != NULL) {
		dpriv->base_addr = be32_to_cpu(val);
		dev_info(dev, "base_addr: 0x%08u", dpriv->base_addr);
	} else {
		dev_err(dev, "can not get register address");
	}
	//of_address_to_resource(devn, 0, &res);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
132 133 134 135 136 137 138 139 140 141 142 143 144

	return 0;
}

static int elphel_drv_probe(struct platform_device *pdev)
{
	int ret;
	struct ahci_host_priv *hpriv;
	struct elphel_ahci_priv *drv_priv;
	struct device *dev = &pdev->dev;
	const struct of_device_id *match;

	struct resource *res;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
145
	unsigned int reg_val;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
146 147

	dev_info(&pdev->dev, "probing Elphel AHCI driver");
148

Mikhail Karpenko's avatar
Mikhail Karpenko committed
149 150 151 152 153 154 155 156 157 158 159 160
	drv_priv = devm_kzalloc(dev, sizeof(struct elphel_ahci_priv), GFP_KERNEL);
	if (!drv_priv)
		return -ENOMEM;

	match = of_match_device(ahci_elphel_of_match, &pdev->dev);
	if (!match)
		return -EINVAL;

	ret = elphel_parse_prop(dev->of_node, dev, drv_priv);
	if (ret != 0)
		return ret;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
161
	hpriv = ahci_platform_get_resources(pdev);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
162 163 164
	if (IS_ERR(hpriv))
		return PTR_ERR(hpriv);

Mikhail Karpenko's avatar
Mikhail Karpenko committed
165
	hpriv->plat_data = drv_priv;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
166

Mikhail Karpenko's avatar
Mikhail Karpenko committed
167 168 169 170 171 172 173 174 175 176 177
	reg_val = readl(hpriv->mmio + HOST_CAP);
	dev_info(dev, "HOST CAP register: 0x%08x", reg_val);
	reg_val = readl(hpriv->mmio + HOST_CTL);
	dev_info(dev, "HOST GHC register: 0x%08x", reg_val);
	reg_val = readl(hpriv->mmio + HOST_IRQ_STAT);
	dev_info(dev, "HOST IS register: 0x%08x", reg_val);
	reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL);
	dev_info(dev, "HOST PI register: 0x%08x", reg_val);
	reg_val = readl(hpriv->mmio + HOST_VERSION);
	dev_info(dev, "HOST VS register: 0x%08x", reg_val);

178 179
	phys_addr_t paddr = virt_to_phys(hpriv->mmio);
	void *vaddr = phys_to_virt(paddr);
180 181 182 183 184
	dev_err(dev, "current mmio virt addr: 0x%p\n", hpriv->mmio);
	dev_err(dev, "current mmio virt addr as uint: 0x%08x\n", hpriv->mmio);
	dev_err(dev, "mmio phys addr: 0x%08x\n", paddr);
	dev_err(dev, "mmio phys addr as tr: 0x%p\n", paddr);
	dev_err(dev, "back converted mmio virt addr: 0x%p\n", vaddr);
185 186 187 188 189 190
	//printk(KERN_DEBUG, "current mmio virt addr: %p\n", hpriv->mmio);
	//printk(KERN_DEBUG, "mmio phys addr: %u\n", paddr);
	//printk(KERN_DEBUG, "back converted mmio virt addr: %p\n", vaddr);
	printk(KERN_DEBUG "======");


Mikhail Karpenko's avatar
Mikhail Karpenko committed
191
	ret = ahci_platform_init_host(pdev, hpriv, &ahci_elphel_port_info,
Mikhail Karpenko's avatar
Mikhail Karpenko committed
192 193
			&ahci_platform_sht);
	if (ret) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
194
		dev_err(dev, "can not initialize platform host");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
195 196 197
		ahci_platform_disable_resources(hpriv);
		return ret;
	}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
198
	dev_info(dev, "ahci platform host initialized");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
199 200 201 202 203 204 205

	return 0;
}

static int elphel_drv_remove(struct platform_device *pdev)
{
	dev_info(&pdev->dev, "removing Elphel AHCI driver");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	ata_platform_remove_one(pdev);
	libahci_debug_exit();

	return 0;
}

static unsigned int elphel_read_id(struct ata_device *dev, struct ata_taskfile *tf, u16 *id)
{
	u32 err_mask;
	struct device *d = &dev->tdev;

	err_mask = ata_do_dev_read_id(dev, tf, id);
	if (err_mask)
		return err_mask;

	dev_info(d, "issue identify command");

	return 0;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
224 225 226 227 228
}

static struct ata_port_operations ahci_elphel_ops = {
		.inherits		= &ahci_ops,
		.port_start		= elphel_port_start,
Mikhail Karpenko's avatar
Mikhail Karpenko committed
229
		.read_id		= elphel_read_id,
Mikhail Karpenko's avatar
Mikhail Karpenko committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
};

static const struct ata_port_info ahci_elphel_port_info = {
		.flags			= AHCI_FLAG_COMMON,
		.pio_mask		= ATA_PIO4,
		.udma_mask		= ATA_UDMA6,
		.port_ops		= &ahci_elphel_ops,
};

static struct scsi_host_template ahci_platform_sht = {
		AHCI_SHT(DRV_NAME),
};

static const struct of_device_id ahci_elphel_of_match[] = {
		{ .compatible = "elphel,elphel-ahci", },
		{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, ahci_elphel_of_match);

static struct platform_driver ahci_elphel_driver = {
		.probe			= elphel_drv_probe,
		/*.remove			= ata_platform_remove_one,*/
		.remove			= elphel_drv_remove,
		.driver	= {
				.name	= DRV_NAME,
				.owner	= THIS_MODULE,
				.of_match_table	= ahci_elphel_of_match,
		},
};
module_platform_driver(ahci_elphel_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Elphel AHCI SATA platform driver for elphel393 camera");