diff options
Diffstat (limited to 'drivers')
35 files changed, 5538 insertions, 68 deletions
diff --git a/drivers/bios_emulator/x86emu/prim_ops.c b/drivers/bios_emulator/x86emu/prim_ops.c index 7553087b2..5f6c795fb 100644 --- a/drivers/bios_emulator/x86emu/prim_ops.c +++ b/drivers/bios_emulator/x86emu/prim_ops.c @@ -118,11 +118,6 @@ static u32 x86emu_parity_tab[8] =  #define PARITY(x)   (((x86emu_parity_tab[(x) / 32] >> ((x) % 32)) & 1) == 0)  #define XOR2(x)	    (((x) ^ ((x)>>1)) & 0x1) -/*----------------------------- Implementation ----------------------------*/ -int abs(int v) -{ -	return (v>0)?v:-v; -}  /*----------------------------- Implementation ----------------------------*/ diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 98560ef76..b9c20475c 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -27,6 +27,7 @@ LIB	:= $(obj)libblock.o  COBJS-$(CONFIG_SCSI_AHCI) += ahci.o  COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o +COBJS-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o  COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o  COBJS-$(CONFIG_IDE_FTIDE020) += ftide020.o  COBJS-$(CONFIG_LIBATA) += libata.o diff --git a/drivers/block/dwc_ahsata.c b/drivers/block/dwc_ahsata.c new file mode 100644 index 000000000..2703d3dc8 --- /dev/null +++ b/drivers/block/dwc_ahsata.c @@ -0,0 +1,969 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Terry Lv <r65388@freescale.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + * + */ + +#include <libata.h> +#include <ahci.h> +#include <fis.h> + +#include <common.h> +#include <malloc.h> +#include <linux/ctype.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <asm/arch/clock.h> +#include "dwc_ahsata.h" + +struct sata_port_regs { +	u32 clb; +	u32 clbu; +	u32 fb; +	u32 fbu; +	u32 is; +	u32 ie; +	u32 cmd; +	u32 res1[1]; +	u32 tfd; +	u32 sig; +	u32 ssts; +	u32 sctl; +	u32 serr; +	u32 sact; +	u32 ci; +	u32 sntf; +	u32 res2[1]; +	u32 dmacr; +	u32 res3[1]; +	u32 phycr; +	u32 physr; +}; + +struct sata_host_regs { +	u32 cap; +	u32 ghc; +	u32 is; +	u32 pi; +	u32 vs; +	u32 ccc_ctl; +	u32 ccc_ports; +	u32 res1[2]; +	u32 cap2; +	u32 res2[30]; +	u32 bistafr; +	u32 bistcr; +	u32 bistfctr; +	u32 bistsr; +	u32 bistdecr; +	u32 res3[2]; +	u32 oobr; +	u32 res4[8]; +	u32 timer1ms; +	u32 res5[1]; +	u32 gparam1r; +	u32 gparam2r; +	u32 pparamr; +	u32 testr; +	u32 versionr; +	u32 idr; +}; + +#define MAX_DATA_BYTES_PER_SG  (4 * 1024 * 1024) +#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG) + +#define writel_with_flush(a, b)	do { writel(a, b); readl(b); } while (0) + +static int is_ready; + +static inline u32 ahci_port_base(u32 base, u32 port) +{ +	return base + 0x100 + (port * 0x80); +} + +static int waiting_for_cmd_completed(u8 *offset, +					int timeout_msec, +					u32 sign) +{ +	int i; +	u32 status; + +	for (i = 0; +		((status = readl(offset)) & sign) && i < timeout_msec; +		++i) +		mdelay(1); + +	return (i < timeout_msec) ? 0 : -1; +} + +static int ahci_setup_oobr(struct ahci_probe_ent *probe_ent, +						int clk) +{ +	struct sata_host_regs *host_mmio = +		(struct sata_host_regs *)probe_ent->mmio_base; + +	writel(SATA_HOST_OOBR_WE, &(host_mmio->oobr)); +	writel(0x02060b14, &(host_mmio->oobr)); + +	return 0; +} + +static int ahci_host_init(struct ahci_probe_ent *probe_ent) +{ +	u32 tmp, cap_save, num_ports; +	int i, j, timeout = 1000; +	struct sata_port_regs *port_mmio = NULL; +	struct sata_host_regs *host_mmio = +		(struct sata_host_regs *)probe_ent->mmio_base; +	int clk = mxc_get_clock(MXC_SATA_CLK); + +	cap_save = readl(&(host_mmio->cap)); +	cap_save |= SATA_HOST_CAP_SSS; + +	/* global controller reset */ +	tmp = readl(&(host_mmio->ghc)); +	if ((tmp & SATA_HOST_GHC_HR) == 0) +		writel_with_flush(tmp | SATA_HOST_GHC_HR, &(host_mmio->ghc)); + +	while ((readl(&(host_mmio->ghc)) & SATA_HOST_GHC_HR) +		&& --timeout) +		; + +	if (timeout <= 0) { +		debug("controller reset failed (0x%x)\n", tmp); +		return -1; +	} + +	/* Set timer 1ms */ +	writel(clk / 1000, &(host_mmio->timer1ms)); + +	ahci_setup_oobr(probe_ent, 0); + +	writel_with_flush(SATA_HOST_GHC_AE, &(host_mmio->ghc)); +	writel(cap_save, &(host_mmio->cap)); +	num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1; +	writel_with_flush((1 << num_ports) - 1, +				&(host_mmio->pi)); + +	/* +	 * Determine which Ports are implemented by the DWC_ahsata, +	 * by reading the PI register. This bit map value aids the +	 * software to determine how many Ports are available and +	 * which Port registers need to be initialized. +	 */ +	probe_ent->cap = readl(&(host_mmio->cap)); +	probe_ent->port_map = readl(&(host_mmio->pi)); + +	/* Determine how many command slots the HBA supports */ +	probe_ent->n_ports = +		(probe_ent->cap & SATA_HOST_CAP_NP_MASK) + 1; + +	debug("cap 0x%x  port_map 0x%x  n_ports %d\n", +		probe_ent->cap, probe_ent->port_map, probe_ent->n_ports); + +	for (i = 0; i < probe_ent->n_ports; i++) { +		probe_ent->port[i].port_mmio = +			ahci_port_base((u32)host_mmio, i); +		port_mmio = +			(struct sata_port_regs *)probe_ent->port[i].port_mmio; + +		/* Ensure that the DWC_ahsata is in idle state */ +		tmp = readl(&(port_mmio->cmd)); + +		/* +		 * When P#CMD.ST, P#CMD.CR, P#CMD.FRE and P#CMD.FR +		 * are all cleared, the Port is in an idle state. +		 */ +		if (tmp & (SATA_PORT_CMD_CR | SATA_PORT_CMD_FR | +			SATA_PORT_CMD_FRE | SATA_PORT_CMD_ST)) { + +			/* +			 * System software places a Port into the idle state by +			 * clearing P#CMD.ST and waiting for P#CMD.CR to return +			 * 0 when read. +			 */ +			tmp &= ~SATA_PORT_CMD_ST; +			writel_with_flush(tmp, &(port_mmio->cmd)); + +			/* +			 * spec says 500 msecs for each bit, so +			 * this is slightly incorrect. +			 */ +			mdelay(500); + +			timeout = 1000; +			while ((readl(&(port_mmio->cmd)) & SATA_PORT_CMD_CR) +				&& --timeout) +				; + +			if (timeout <= 0) { +				debug("port reset failed (0x%x)\n", tmp); +				return -1; +			} +		} + +		/* Spin-up device */ +		tmp = readl(&(port_mmio->cmd)); +		writel((tmp | SATA_PORT_CMD_SUD), &(port_mmio->cmd)); + +		/* Wait for spin-up to finish */ +		timeout = 1000; +		while (!(readl(&(port_mmio->cmd)) | SATA_PORT_CMD_SUD) +			&& --timeout) +			; +		if (timeout <= 0) { +			debug("Spin-Up can't finish!\n"); +			return -1; +		} + +		for (j = 0; j < 100; ++j) { +			mdelay(10); +			tmp = readl(&(port_mmio->ssts)); +			if (((tmp & SATA_PORT_SSTS_DET_MASK) == 0x3) || +				((tmp & SATA_PORT_SSTS_DET_MASK) == 0x1)) +				break; +		} + +		/* Wait for COMINIT bit 26 (DIAG_X) in SERR */ +		timeout = 1000; +		while (!(readl(&(port_mmio->serr)) | SATA_PORT_SERR_DIAG_X) +			&& --timeout) +			; +		if (timeout <= 0) { +			debug("Can't find DIAG_X set!\n"); +			return -1; +		} + +		/* +		 * For each implemented Port, clear the P#SERR +		 * register, by writing ones to each implemented\ +		 * bit location. +		 */ +		tmp = readl(&(port_mmio->serr)); +		debug("P#SERR 0x%x\n", +				tmp); +		writel(tmp, &(port_mmio->serr)); + +		/* Ack any pending irq events for this port */ +		tmp = readl(&(host_mmio->is)); +		debug("IS 0x%x\n", tmp); +		if (tmp) +			writel(tmp, &(host_mmio->is)); + +		writel(1 << i, &(host_mmio->is)); + +		/* set irq mask (enables interrupts) */ +		writel(DEF_PORT_IRQ, &(port_mmio->ie)); + +		/* register linkup ports */ +		tmp = readl(&(port_mmio->ssts)); +		debug("Port %d status: 0x%x\n", i, tmp); +		if ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x03) +			probe_ent->link_port_map |= (0x01 << i); +	} + +	tmp = readl(&(host_mmio->ghc)); +	debug("GHC 0x%x\n", tmp); +	writel(tmp | SATA_HOST_GHC_IE, &(host_mmio->ghc)); +	tmp = readl(&(host_mmio->ghc)); +	debug("GHC 0x%x\n", tmp); + +	return 0; +} + +static void ahci_print_info(struct ahci_probe_ent *probe_ent) +{ +	struct sata_host_regs *host_mmio = +		(struct sata_host_regs *)probe_ent->mmio_base; +	u32 vers, cap, impl, speed; +	const char *speed_s; +	const char *scc_s; + +	vers = readl(&(host_mmio->vs)); +	cap = probe_ent->cap; +	impl = probe_ent->port_map; + +	speed = (cap & SATA_HOST_CAP_ISS_MASK) +		>> SATA_HOST_CAP_ISS_OFFSET; +	if (speed == 1) +		speed_s = "1.5"; +	else if (speed == 2) +		speed_s = "3"; +	else +		speed_s = "?"; + +	scc_s = "SATA"; + +	printf("AHCI %02x%02x.%02x%02x " +		"%u slots %u ports %s Gbps 0x%x impl %s mode\n", +		(vers >> 24) & 0xff, +		(vers >> 16) & 0xff, +		(vers >> 8) & 0xff, +		vers & 0xff, +		((cap >> 8) & 0x1f) + 1, +		(cap & 0x1f) + 1, +		speed_s, +		impl, +		scc_s); + +	printf("flags: " +		"%s%s%s%s%s%s" +		"%s%s%s%s%s%s%s\n", +		cap & (1 << 31) ? "64bit " : "", +		cap & (1 << 30) ? "ncq " : "", +		cap & (1 << 28) ? "ilck " : "", +		cap & (1 << 27) ? "stag " : "", +		cap & (1 << 26) ? "pm " : "", +		cap & (1 << 25) ? "led " : "", +		cap & (1 << 24) ? "clo " : "", +		cap & (1 << 19) ? "nz " : "", +		cap & (1 << 18) ? "only " : "", +		cap & (1 << 17) ? "pmp " : "", +		cap & (1 << 15) ? "pio " : "", +		cap & (1 << 14) ? "slum " : "", +		cap & (1 << 13) ? "part " : ""); +} + +static int ahci_init_one(int pdev) +{ +	int rc; +	struct ahci_probe_ent *probe_ent = NULL; + +	probe_ent = malloc(sizeof(struct ahci_probe_ent)); +	memset(probe_ent, 0, sizeof(struct ahci_probe_ent)); +	probe_ent->dev = pdev; + +	probe_ent->host_flags = ATA_FLAG_SATA +				| ATA_FLAG_NO_LEGACY +				| ATA_FLAG_MMIO +				| ATA_FLAG_PIO_DMA +				| ATA_FLAG_NO_ATAPI; + +	probe_ent->mmio_base = CONFIG_DWC_AHSATA_BASE_ADDR; + +	/* initialize adapter */ +	rc = ahci_host_init(probe_ent); +	if (rc) +		goto err_out; + +	ahci_print_info(probe_ent); + +	/* Save the private struct to block device struct */ +	sata_dev_desc[pdev].priv = (void *)probe_ent; + +	return 0; + +err_out: +	return rc; +} + +static int ahci_fill_sg(struct ahci_probe_ent *probe_ent, +			u8 port, unsigned char *buf, int buf_len) +{ +	struct ahci_ioports *pp = &(probe_ent->port[port]); +	struct ahci_sg *ahci_sg = pp->cmd_tbl_sg; +	u32 sg_count, max_bytes; +	int i; + +	max_bytes = MAX_DATA_BYTES_PER_SG; +	sg_count = ((buf_len - 1) / max_bytes) + 1; +	if (sg_count > AHCI_MAX_SG) { +		printf("Error:Too much sg!\n"); +		return -1; +	} + +	for (i = 0; i < sg_count; i++) { +		ahci_sg->addr = +			cpu_to_le32((u32)buf + i * max_bytes); +		ahci_sg->addr_hi = 0; +		ahci_sg->flags_size = cpu_to_le32(0x3fffff & +					(buf_len < max_bytes +					? (buf_len - 1) +					: (max_bytes - 1))); +		ahci_sg++; +		buf_len -= max_bytes; +	} + +	return sg_count; +} + +static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts) +{ +	struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot + +					AHCI_CMD_SLOT_SZ * cmd_slot); + +	memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ); +	cmd_hdr->opts = cpu_to_le32(opts); +	cmd_hdr->status = 0; +	cmd_hdr->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff); +	cmd_hdr->tbl_addr_hi = 0; +} + +#define AHCI_GET_CMD_SLOT(c) ((c) ? ffs(c) : 0) + +static int ahci_exec_ata_cmd(struct ahci_probe_ent *probe_ent, +		u8 port, struct sata_fis_h2d *cfis, +		u8 *buf, u32 buf_len, s32 is_write) +{ +	struct ahci_ioports *pp = &(probe_ent->port[port]); +	struct sata_port_regs *port_mmio = +			(struct sata_port_regs *)pp->port_mmio; +	u32 opts; +	int sg_count = 0, cmd_slot = 0; + +	cmd_slot = AHCI_GET_CMD_SLOT(readl(&(port_mmio->ci))); +	if (32 == cmd_slot) { +		printf("Can't find empty command slot!\n"); +		return 0; +	} + +	/* Check xfer length */ +	if (buf_len > MAX_BYTES_PER_TRANS) { +		printf("Max transfer length is %dB\n\r", +			MAX_BYTES_PER_TRANS); +		return 0; +	} + +	memcpy((u8 *)(pp->cmd_tbl), cfis, sizeof(struct sata_fis_h2d)); +	if (buf && buf_len) +		sg_count = ahci_fill_sg(probe_ent, port, buf, buf_len); +	opts = (sizeof(struct sata_fis_h2d) >> 2) | (sg_count << 16); +	if (is_write) +		opts |= 0x40; +	ahci_fill_cmd_slot(pp, cmd_slot, opts); + +	writel_with_flush(1 << cmd_slot, &(port_mmio->ci)); + +	if (waiting_for_cmd_completed((u8 *)&(port_mmio->ci), +				10000, 0x1 << cmd_slot)) { +		printf("timeout exit!\n"); +		return -1; +	} +	debug("ahci_exec_ata_cmd: %d byte transferred.\n", +	      pp->cmd_slot->status); + +	return buf_len; +} + +static void ahci_set_feature(u8 dev, u8 port) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 1 << 7; +	cfis->command = ATA_CMD_SET_FEATURES; +	cfis->features = SETFEATURES_XFER; +	cfis->sector_count = ffs(probe_ent->udma_mask + 1) + 0x3e; + +	ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, READ_CMD); +} + +static int ahci_port_start(struct ahci_probe_ent *probe_ent, +					u8 port) +{ +	struct ahci_ioports *pp = &(probe_ent->port[port]); +	struct sata_port_regs *port_mmio = +		(struct sata_port_regs *)pp->port_mmio; +	u32 port_status; +	u32 mem; +	int timeout = 10000000; + +	debug("Enter start port: %d\n", port); +	port_status = readl(&(port_mmio->ssts)); +	debug("Port %d status: %x\n", port, port_status); +	if ((port_status & 0xf) != 0x03) { +		printf("No Link on this port!\n"); +		return -1; +	} + +	mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024); +	if (!mem) { +		free(pp); +		printf("No mem for table!\n"); +		return -ENOMEM; +	} + +	mem = (mem + 0x400) & (~0x3ff);	/* Aligned to 1024-bytes */ +	memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ); + +	/* +	 * First item in chunk of DMA memory: 32-slot command table, +	 * 32 bytes each in size +	 */ +	pp->cmd_slot = (struct ahci_cmd_hdr *)mem; +	debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot); +	mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS); + +	/* +	 * Second item: Received-FIS area, 256-Byte aligned +	 */ +	pp->rx_fis = mem; +	mem += AHCI_RX_FIS_SZ; + +	/* +	 * Third item: data area for storing a single command +	 * and its scatter-gather table +	 */ +	pp->cmd_tbl = mem; +	debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl); + +	mem += AHCI_CMD_TBL_HDR; + +	writel_with_flush(0x00004444, &(port_mmio->dmacr)); +	pp->cmd_tbl_sg = (struct ahci_sg *)mem; +	writel_with_flush((u32)pp->cmd_slot, &(port_mmio->clb)); +	writel_with_flush(pp->rx_fis, &(port_mmio->fb)); + +	/* Enable FRE */ +	writel_with_flush((SATA_PORT_CMD_FRE | readl(&(port_mmio->cmd))), +			&(port_mmio->cmd)); + +	/* Wait device ready */ +	while ((readl(&(port_mmio->tfd)) & (SATA_PORT_TFD_STS_ERR | +		SATA_PORT_TFD_STS_DRQ | SATA_PORT_TFD_STS_BSY)) +		&& --timeout) +		; +	if (timeout <= 0) { +		debug("Device not ready for BSY, DRQ and" +			"ERR in TFD!\n"); +		return -1; +	} + +	writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | +			  PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | +			  PORT_CMD_START, &(port_mmio->cmd)); + +	debug("Exit start port %d\n", port); + +	return 0; +} + +int init_sata(int dev) +{ +	int i; +	u32 linkmap; +	struct ahci_probe_ent *probe_ent = NULL; + +	if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) { +		printf("The sata index %d is out of ranges\n\r", dev); +		return -1; +	} + +	ahci_init_one(dev); + +	probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	linkmap = probe_ent->link_port_map; + +	if (0 == linkmap) { +		printf("No port device detected!\n"); +		return 1; +	} + +	for (i = 0; i < probe_ent->n_ports; i++) { +		if ((linkmap >> i) && ((linkmap >> i) & 0x01)) { +			if (ahci_port_start(probe_ent, (u8)i)) { +				printf("Can not start port %d\n", i); +				return 1; +			} +			probe_ent->hard_port_no = i; +			break; +		} +	} + +	return 0; +} + +static void dwc_ahsata_print_info(int dev) +{ +	block_dev_desc_t *pdev = &(sata_dev_desc[dev]); + +	printf("SATA Device Info:\n\r"); +#ifdef CONFIG_SYS_64BIT_LBA +	printf("S/N: %s\n\rProduct model number: %s\n\r" +		"Firmware version: %s\n\rCapacity: %lld sectors\n\r", +		pdev->product, pdev->vendor, pdev->revision, pdev->lba); +#else +	printf("S/N: %s\n\rProduct model number: %s\n\r" +		"Firmware version: %s\n\rCapacity: %ld sectors\n\r", +		pdev->product, pdev->vendor, pdev->revision, pdev->lba); +#endif +} + +static void dwc_ahsata_identify(int dev, u16 *id) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; +	u8 port = probe_ent->hard_port_no; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); + +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 0x80; /* is command */ +	cfis->command = ATA_CMD_ID_ATA; + +	ahci_exec_ata_cmd(probe_ent, port, cfis, +			(u8 *)id, ATA_ID_WORDS * 2, READ_CMD); +	ata_swap_buf_le16(id, ATA_ID_WORDS); +} + +static void dwc_ahsata_xfer_mode(int dev, u16 *id) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; + +	probe_ent->pio_mask = id[ATA_ID_PIO_MODES]; +	probe_ent->udma_mask = id[ATA_ID_UDMA_MODES]; +	debug("pio %04x, udma %04x\n\r", +		probe_ent->pio_mask, probe_ent->udma_mask); +} + +static u32 dwc_ahsata_rw_cmd(int dev, u32 start, u32 blkcnt, +				u8 *buffer, int is_write) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; +	u8 port = probe_ent->hard_port_no; +	u32 block; + +	block = start; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); + +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 0x80; /* is command */ +	cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ; +	cfis->device = ATA_LBA; + +	cfis->device |= (block >> 24) & 0xf; +	cfis->lba_high = (block >> 16) & 0xff; +	cfis->lba_mid = (block >> 8) & 0xff; +	cfis->lba_low = block & 0xff; +	cfis->sector_count = (u8)(blkcnt & 0xff); + +	if (ahci_exec_ata_cmd(probe_ent, port, cfis, +			buffer, ATA_SECT_SIZE * blkcnt, is_write) > 0) +		return blkcnt; +	else +		return 0; +} + +void dwc_ahsata_flush_cache(int dev) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; +	u8 port = probe_ent->hard_port_no; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); + +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 0x80; /* is command */ +	cfis->command = ATA_CMD_FLUSH; + +	ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0); +} + +static u32 dwc_ahsata_rw_cmd_ext(int dev, u32 start, lbaint_t blkcnt, +				u8 *buffer, int is_write) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; +	u8 port = probe_ent->hard_port_no; +	u64 block; + +	block = (u64)start; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); + +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 0x80; /* is command */ + +	cfis->command = (is_write) ? ATA_CMD_WRITE_EXT +				 : ATA_CMD_READ_EXT; + +	cfis->lba_high_exp = (block >> 40) & 0xff; +	cfis->lba_mid_exp = (block >> 32) & 0xff; +	cfis->lba_low_exp = (block >> 24) & 0xff; +	cfis->lba_high = (block >> 16) & 0xff; +	cfis->lba_mid = (block >> 8) & 0xff; +	cfis->lba_low = block & 0xff; +	cfis->device = ATA_LBA; +	cfis->sector_count_exp = (blkcnt >> 8) & 0xff; +	cfis->sector_count = blkcnt & 0xff; + +	if (ahci_exec_ata_cmd(probe_ent, port, cfis, buffer, +			ATA_SECT_SIZE * blkcnt, is_write) > 0) +		return blkcnt; +	else +		return 0; +} + +u32 dwc_ahsata_rw_ncq_cmd(int dev, u32 start, lbaint_t blkcnt, +				u8 *buffer, int is_write) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; +	u8 port = probe_ent->hard_port_no; +	u64 block; + +	if (sata_dev_desc[dev].lba48 != 1) { +		printf("execute FPDMA command on non-LBA48 hard disk\n\r"); +		return -1; +	} + +	block = (u64)start; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); + +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 0x80; /* is command */ + +	cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE +				 : ATA_CMD_FPDMA_READ; + +	cfis->lba_high_exp = (block >> 40) & 0xff; +	cfis->lba_mid_exp = (block >> 32) & 0xff; +	cfis->lba_low_exp = (block >> 24) & 0xff; +	cfis->lba_high = (block >> 16) & 0xff; +	cfis->lba_mid = (block >> 8) & 0xff; +	cfis->lba_low = block & 0xff; + +	cfis->device = ATA_LBA; +	cfis->features_exp = (blkcnt >> 8) & 0xff; +	cfis->features = blkcnt & 0xff; + +	/* Use the latest queue */ +	ahci_exec_ata_cmd(probe_ent, port, cfis, +			buffer, ATA_SECT_SIZE * blkcnt, is_write); + +	return blkcnt; +} + +void dwc_ahsata_flush_cache_ext(int dev) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	struct sata_fis_h2d h2d, *cfis = &h2d; +	u8 port = probe_ent->hard_port_no; + +	memset(cfis, 0, sizeof(struct sata_fis_h2d)); + +	cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; +	cfis->pm_port_c = 0x80; /* is command */ +	cfis->command = ATA_CMD_FLUSH_EXT; + +	ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0); +} + +static void dwc_ahsata_init_wcache(int dev, u16 *id) +{ +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; + +	if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id)) +		probe_ent->flags |= SATA_FLAG_WCACHE; +	if (ata_id_has_flush(id)) +		probe_ent->flags |= SATA_FLAG_FLUSH; +	if (ata_id_has_flush_ext(id)) +		probe_ent->flags |= SATA_FLAG_FLUSH_EXT; +} + +u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt, +				void *buffer, int is_write) +{ +	u32 start, blks; +	u8 *addr; +	int max_blks; + +	start = blknr; +	blks = blkcnt; +	addr = (u8 *)buffer; + +	max_blks = ATA_MAX_SECTORS_LBA48; + +	do { +		if (blks > max_blks) { +			if (max_blks != dwc_ahsata_rw_cmd_ext(dev, start, +						max_blks, addr, is_write)) +				return 0; +			start += max_blks; +			blks -= max_blks; +			addr += ATA_SECT_SIZE * max_blks; +		} else { +			if (blks != dwc_ahsata_rw_cmd_ext(dev, start, +						blks, addr, is_write)) +				return 0; +			start += blks; +			blks = 0; +			addr += ATA_SECT_SIZE * blks; +		} +	} while (blks != 0); + +	return blkcnt; +} + +u32 ata_low_level_rw_lba28(int dev, u32 blknr, lbaint_t blkcnt, +				void *buffer, int is_write) +{ +	u32 start, blks; +	u8 *addr; +	int max_blks; + +	start = blknr; +	blks = blkcnt; +	addr = (u8 *)buffer; + +	max_blks = ATA_MAX_SECTORS; +	do { +		if (blks > max_blks) { +			if (max_blks != dwc_ahsata_rw_cmd(dev, start, +						max_blks, addr, is_write)) +				return 0; +			start += max_blks; +			blks -= max_blks; +			addr += ATA_SECT_SIZE * max_blks; +		} else { +			if (blks != dwc_ahsata_rw_cmd(dev, start, +						blks, addr, is_write)) +				return 0; +			start += blks; +			blks = 0; +			addr += ATA_SECT_SIZE * blks; +		} +	} while (blks != 0); + +	return blkcnt; +} + +/* + * SATA interface between low level driver and command layer + */ +ulong sata_read(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer) +{ +	u32 rc; + +	if (sata_dev_desc[dev].lba48) +		rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, +						buffer, READ_CMD); +	else +		rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, +						buffer, READ_CMD); +	return rc; +} + +ulong sata_write(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer) +{ +	u32 rc; +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	u32 flags = probe_ent->flags; + +	if (sata_dev_desc[dev].lba48) { +		rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, +						buffer, WRITE_CMD); +		if ((flags & SATA_FLAG_WCACHE) && +			(flags & SATA_FLAG_FLUSH_EXT)) +			dwc_ahsata_flush_cache_ext(dev); +	} else { +		rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, +						buffer, WRITE_CMD); +		if ((flags & SATA_FLAG_WCACHE) && +			(flags & SATA_FLAG_FLUSH)) +			dwc_ahsata_flush_cache(dev); +	} +	return rc; +} + +int scan_sata(int dev) +{ +	u8 serial[ATA_ID_SERNO_LEN + 1] = { 0 }; +	u8 firmware[ATA_ID_FW_REV_LEN + 1] = { 0 }; +	u8 product[ATA_ID_PROD_LEN + 1] = { 0 }; +	u16 *id; +	u64 n_sectors; +	struct ahci_probe_ent *probe_ent = +		(struct ahci_probe_ent *)sata_dev_desc[dev].priv; +	u8 port = probe_ent->hard_port_no; +	block_dev_desc_t *pdev = &(sata_dev_desc[dev]); + +	id = (u16 *)malloc(ATA_ID_WORDS * 2); +	if (!id) { +		printf("id malloc failed\n\r"); +		return -1; +	} + +	/* Identify device to get information */ +	dwc_ahsata_identify(dev, id); + +	/* Serial number */ +	ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); +	memcpy(pdev->product, serial, sizeof(serial)); + +	/* Firmware version */ +	ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); +	memcpy(pdev->revision, firmware, sizeof(firmware)); + +	/* Product model */ +	ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); +	memcpy(pdev->vendor, product, sizeof(product)); + +	/* Totoal sectors */ +	n_sectors = ata_id_n_sectors(id); +	pdev->lba = (u32)n_sectors; + +	pdev->type = DEV_TYPE_HARDDISK; +	pdev->blksz = ATA_SECT_SIZE; +	pdev->lun = 0 ; + +	/* Check if support LBA48 */ +	if (ata_id_has_lba48(id)) { +		pdev->lba48 = 1; +		debug("Device support LBA48\n\r"); +	} + +	/* Get the NCQ queue depth from device */ +	probe_ent->flags &= (~SATA_FLAG_Q_DEP_MASK); +	probe_ent->flags |= ata_id_queue_depth(id); + +	/* Get the xfer mode from device */ +	dwc_ahsata_xfer_mode(dev, id); + +	/* Get the write cache status from device */ +	dwc_ahsata_init_wcache(dev, id); + +	/* Set the xfer mode to highest speed */ +	ahci_set_feature(dev, port); + +	free((void *)id); + +	dwc_ahsata_print_info(dev); + +	is_ready = 1; + +	return 0; +} diff --git a/drivers/block/dwc_ahsata.h b/drivers/block/dwc_ahsata.h new file mode 100644 index 000000000..84860ea49 --- /dev/null +++ b/drivers/block/dwc_ahsata.h @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Terry Lv <r65388@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __FSL_SATA_H__ +#define __FSL_SATA_H__ + +#define DWC_AHSATA_MAX_CMD_SLOTS	32 + +/* Max host controller numbers */ +#define SATA_HC_MAX_NUM		4 +/* Max command queue depth per host controller */ +#define DWC_AHSATA_HC_MAX_CMD	32 +/* Max port number per host controller */ +#define SATA_HC_MAX_PORT	16 + +/* Generic Host Register */ + +/* HBA Capabilities Register */ +#define SATA_HOST_CAP_S64A		0x80000000 +#define SATA_HOST_CAP_SNCQ		0x40000000 +#define SATA_HOST_CAP_SSNTF		0x20000000 +#define SATA_HOST_CAP_SMPS		0x10000000 +#define SATA_HOST_CAP_SSS		0x08000000 +#define SATA_HOST_CAP_SALP		0x04000000 +#define SATA_HOST_CAP_SAL		0x02000000 +#define SATA_HOST_CAP_SCLO		0x01000000 +#define SATA_HOST_CAP_ISS_MASK		0x00f00000 +#define SATA_HOST_CAP_ISS_OFFSET	20 +#define SATA_HOST_CAP_SNZO		0x00080000 +#define SATA_HOST_CAP_SAM		0x00040000 +#define SATA_HOST_CAP_SPM		0x00020000 +#define SATA_HOST_CAP_PMD		0x00008000 +#define SATA_HOST_CAP_SSC		0x00004000 +#define SATA_HOST_CAP_PSC		0x00002000 +#define SATA_HOST_CAP_NCS		0x00001f00 +#define SATA_HOST_CAP_CCCS		0x00000080 +#define SATA_HOST_CAP_EMS		0x00000040 +#define SATA_HOST_CAP_SXS		0x00000020 +#define SATA_HOST_CAP_NP_MASK		0x0000001f + +/* Global HBA Control Register */ +#define SATA_HOST_GHC_AE	0x80000000 +#define SATA_HOST_GHC_IE	0x00000002 +#define SATA_HOST_GHC_HR	0x00000001 + +/* Interrupt Status Register */ + +/* Ports Implemented Register */ + +/* AHCI Version Register */ +#define SATA_HOST_VS_MJR_MASK	0xffff0000 +#define SATA_HOST_VS_MJR_OFFSET	16 +#define SATA_HOST_VS_MJR_MNR	0x0000ffff + +/* Command Completion Coalescing Control */ +#define SATA_HOST_CCC_CTL_TV_MASK	0xffff0000 +#define SATA_HOST_CCC_CTL_TV_OFFSET		16 +#define SATA_HOST_CCC_CTL_CC_MASK	0x0000ff00 +#define SATA_HOST_CCC_CTL_CC_OFFSET		8 +#define SATA_HOST_CCC_CTL_INT_MASK	0x000000f8 +#define SATA_HOST_CCC_CTL_INT_OFFSET	3 +#define SATA_HOST_CCC_CTL_EN	0x00000001 + +/* Command Completion Coalescing Ports */ + +/* HBA Capabilities Extended Register */ +#define SATA_HOST_CAP2_APST		0x00000004 + +/* BIST Activate FIS Register */ +#define SATA_HOST_BISTAFR_NCP_MASK	0x0000ff00 +#define SATA_HOST_BISTAFR_NCP_OFFSET	8 +#define SATA_HOST_BISTAFR_PD_MASK	0x000000ff +#define SATA_HOST_BISTAFR_PD_OFFSET		0 + +/* BIST Control Register */ +#define SATA_HOST_BISTCR_FERLB	0x00100000 +#define SATA_HOST_BISTCR_TXO	0x00040000 +#define SATA_HOST_BISTCR_CNTCLR	0x00020000 +#define SATA_HOST_BISTCR_NEALB	0x00010000 +#define SATA_HOST_BISTCR_LLC_MASK	0x00000700 +#define SATA_HOST_BISTCR_LLC_OFFSET	8 +#define SATA_HOST_BISTCR_ERREN	0x00000040 +#define SATA_HOST_BISTCR_FLIP	0x00000020 +#define SATA_HOST_BISTCR_PV		0x00000010 +#define SATA_HOST_BISTCR_PATTERN_MASK	0x0000000f +#define SATA_HOST_BISTCR_PATTERN_OFFSET	0 + +/* BIST FIS Count Register */ + +/* BIST Status Register */ +#define SATA_HOST_BISTSR_FRAMERR_MASK	0x0000ffff +#define SATA_HOST_BISTSR_FRAMERR_OFFSET	0 +#define SATA_HOST_BISTSR_BRSTERR_MASK	0x00ff0000 +#define SATA_HOST_BISTSR_BRSTERR_OFFSET	16 + +/* BIST DWORD Error Count Register */ + +/* OOB Register*/ +#define SATA_HOST_OOBR_WE		0x80000000 +#define SATA_HOST_OOBR_cwMin_MASK	0x7f000000 +#define SATA_HOST_OOBR_cwMAX_MASK	0x00ff0000 +#define SATA_HOST_OOBR_ciMin_MASK	0x0000ff00 +#define SATA_HOST_OOBR_ciMax_MASK	0x000000ff + +/* Timer 1-ms Register */ + +/* Global Parameter 1 Register */ +#define SATA_HOST_GPARAM1R_ALIGN_M	0x80000000 +#define SATA_HOST_GPARAM1R_RX_BUFFER	0x40000000 +#define SATA_HOST_GPARAM1R_PHY_DATA_MASK	0x30000000 +#define SATA_HOST_GPARAM1R_PHY_RST	0x08000000 +#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK	0x07e00000 +#define SATA_HOST_GPARAM1R_PHY_STAT_MASK	0x001f8000 +#define SATA_HOST_GPARAM1R_LATCH_M	0x00004000 +#define SATA_HOST_GPARAM1R_BIST_M	0x00002000 +#define SATA_HOST_GPARAM1R_PHY_TYPE	0x00001000 +#define SATA_HOST_GPARAM1R_RETURN_ERR	0x00000400 +#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK	0x00000300 +#define SATA_HOST_GPARAM1R_S_HADDR	0X00000080 +#define SATA_HOST_GPARAM1R_M_HADDR	0X00000040 + +/* Global Parameter 2 Register */ +#define SATA_HOST_GPARAM2R_DEV_CP	0x00004000 +#define SATA_HOST_GPARAM2R_DEV_MP	0x00002000 +#define SATA_HOST_GPARAM2R_DEV_ENCODE_M	0x00001000 +#define SATA_HOST_GPARAM2R_RXOOB_CLK_M	0x00000800 +#define SATA_HOST_GPARAM2R_RXOOB_M	0x00000400 +#define SATA_HOST_GPARAM2R_TX_OOB_M	0x00000200 +#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK	0x000001ff + +/* Port Parameter Register */ +#define SATA_HOST_PPARAMR_TX_MEM_M	0x00000200 +#define SATA_HOST_PPARAMR_TX_MEM_S	0x00000100 +#define SATA_HOST_PPARAMR_RX_MEM_M	0x00000080 +#define SATA_HOST_PPARAMR_RX_MEM_S	0x00000040 +#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK	0x00000038 +#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK	0x00000007 + +/* Test Register */ +#define SATA_HOST_TESTR_PSEL_MASK	0x00070000 +#define SATA_HOST_TESTR_TEST_IF		0x00000001 + +/* Port Register Descriptions */ +/* Port# Command List Base Address Register */ +#define SATA_PORT_CLB_CLB_MASK		0xfffffc00 + +/* Port# Command List Base Address Upper 32-Bits Register */ + +/* Port# FIS Base Address Register */ +#define SATA_PORT_FB_FB_MASK		0xfffffff0 + +/* Port# FIS Base Address Upper 32-Bits Register */ + +/* Port# Interrupt Status Register */ +#define SATA_PORT_IS_CPDS		0x80000000 +#define SATA_PORT_IS_TFES		0x40000000 +#define SATA_PORT_IS_HBFS		0x20000000 +#define SATA_PORT_IS_HBDS		0x10000000 +#define SATA_PORT_IS_IFS		0x08000000 +#define SATA_PORT_IS_INFS		0x04000000 +#define SATA_PORT_IS_OFS		0x01000000 +#define SATA_PORT_IS_IPMS		0x00800000 +#define SATA_PORT_IS_PRCS		0x00400000 +#define SATA_PORT_IS_DMPS		0x00000080 +#define SATA_PORT_IS_PCS		0x00000040 +#define SATA_PORT_IS_DPS		0x00000020 +#define SATA_PORT_IS_UFS		0x00000010 +#define SATA_PORT_IS_SDBS		0x00000008 +#define SATA_PORT_IS_DSS		0x00000004 +#define SATA_PORT_IS_PSS		0x00000002 +#define SATA_PORT_IS_DHRS		0x00000001 + +/* Port# Interrupt Enable Register */ +#define SATA_PORT_IE_CPDE		0x80000000 +#define SATA_PORT_IE_TFEE		0x40000000 +#define SATA_PORT_IE_HBFE		0x20000000 +#define SATA_PORT_IE_HBDE		0x10000000 +#define SATA_PORT_IE_IFE		0x08000000 +#define SATA_PORT_IE_INFE		0x04000000 +#define SATA_PORT_IE_OFE		0x01000000 +#define SATA_PORT_IE_IPME		0x00800000 +#define SATA_PORT_IE_PRCE		0x00400000 +#define SATA_PORT_IE_DMPE		0x00000080 +#define SATA_PORT_IE_PCE		0x00000040 +#define SATA_PORT_IE_DPE		0x00000020 +#define SATA_PORT_IE_UFE		0x00000010 +#define SATA_PORT_IE_SDBE		0x00000008 +#define SATA_PORT_IE_DSE		0x00000004 +#define SATA_PORT_IE_PSE		0x00000002 +#define SATA_PORT_IE_DHRE		0x00000001 + +/* Port# Command Register */ +#define SATA_PORT_CMD_ICC_MASK		0xf0000000 +#define SATA_PORT_CMD_ASP		0x08000000 +#define SATA_PORT_CMD_ALPE		0x04000000 +#define SATA_PORT_CMD_DLAE		0x02000000 +#define SATA_PORT_CMD_ATAPI		0x01000000 +#define SATA_PORT_CMD_APSTE		0x00800000 +#define SATA_PORT_CMD_ESP		0x00200000 +#define SATA_PORT_CMD_CPD		0x00100000 +#define SATA_PORT_CMD_MPSP		0x00080000 +#define SATA_PORT_CMD_HPCP		0x00040000 +#define SATA_PORT_CMD_PMA		0x00020000 +#define SATA_PORT_CMD_CPS		0x00010000 +#define SATA_PORT_CMD_CR		0x00008000 +#define SATA_PORT_CMD_FR		0x00004000 +#define SATA_PORT_CMD_MPSS		0x00002000 +#define SATA_PORT_CMD_CCS_MASK		0x00001f00 +#define SATA_PORT_CMD_FRE		0x00000010 +#define SATA_PORT_CMD_CLO		0x00000008 +#define SATA_PORT_CMD_POD		0x00000004 +#define SATA_PORT_CMD_SUD		0x00000002 +#define SATA_PORT_CMD_ST		0x00000001 + +/* Port# Task File Data Register */ +#define SATA_PORT_TFD_ERR_MASK		0x0000ff00 +#define SATA_PORT_TFD_STS_MASK		0x000000ff +#define SATA_PORT_TFD_STS_ERR		0x00000001 +#define SATA_PORT_TFD_STS_DRQ		0x00000008 +#define SATA_PORT_TFD_STS_BSY		0x00000080 + +/* Port# Signature Register */ + +/* Port# Serial ATA Status {SStatus} Register */ +#define SATA_PORT_SSTS_IPM_MASK		0x00000f00 +#define SATA_PORT_SSTS_SPD_MASK		0x000000f0 +#define SATA_PORT_SSTS_DET_MASK		0x0000000f + +/* Port# Serial ATA Control {SControl} Register */ +#define SATA_PORT_SCTL_IPM_MASK		0x00000f00 +#define SATA_PORT_SCTL_SPD_MASK		0x000000f0 +#define SATA_PORT_SCTL_DET_MASK		0x0000000f + +/* Port# Serial ATA Error {SError} Register */ +#define SATA_PORT_SERR_DIAG_X		0x04000000 +#define SATA_PORT_SERR_DIAG_F		0x02000000 +#define SATA_PORT_SERR_DIAG_T		0x01000000 +#define SATA_PORT_SERR_DIAG_S		0x00800000 +#define SATA_PORT_SERR_DIAG_H		0x00400000 +#define SATA_PORT_SERR_DIAG_C		0x00200000 +#define SATA_PORT_SERR_DIAG_D		0x00100000 +#define SATA_PORT_SERR_DIAG_B		0x00080000 +#define SATA_PORT_SERR_DIAG_W		0x00040000 +#define SATA_PORT_SERR_DIAG_I		0x00020000 +#define SATA_PORT_SERR_DIAG_N		0x00010000 +#define SATA_PORT_SERR_ERR_E		0x00000800 +#define SATA_PORT_SERR_ERR_P		0x00000400 +#define SATA_PORT_SERR_ERR_C		0x00000200 +#define SATA_PORT_SERR_ERR_T		0x00000100 +#define SATA_PORT_SERR_ERR_M		0x00000002 +#define SATA_PORT_SERR_ERR_I		0x00000001 + +/* Port# Serial ATA Active {SActive} Register */ + +/* Port# Command Issue Register */ + +/* Port# Serial ATA Notification Register */ + +/* Port# DMA Control Register */ +#define SATA_PORT_DMACR_RXABL_MASK	0x0000f000 +#define SATA_PORT_DMACR_TXABL_MASK	0x00000f00 +#define SATA_PORT_DMACR_RXTS_MASK	0x000000f0 +#define SATA_PORT_DMACR_TXTS_MASK	0x0000000f + +/* Port# PHY Control Register */ + +/* Port# PHY Status Register */ + +#define SATA_HC_CMD_HDR_ENTRY_SIZE	sizeof(struct cmd_hdr_entry) + +/* DW0 +*/ +#define CMD_HDR_DI_CFL_MASK	0x0000001f +#define CMD_HDR_DI_CFL_OFFSET	0 +#define CMD_HDR_DI_A			0x00000020 +#define CMD_HDR_DI_W			0x00000040 +#define CMD_HDR_DI_P			0x00000080 +#define CMD_HDR_DI_R			0x00000100 +#define CMD_HDR_DI_B			0x00000200 +#define CMD_HDR_DI_C			0x00000400 +#define CMD_HDR_DI_PMP_MASK	0x0000f000 +#define CMD_HDR_DI_PMP_OFFSET	12 +#define CMD_HDR_DI_PRDTL		0xffff0000 +#define CMD_HDR_DI_PRDTL_OFFSET	16 + +/* prde_fis_len +*/ +#define CMD_HDR_PRD_ENTRY_SHIFT	16 +#define CMD_HDR_PRD_ENTRY_MASK	0x003f0000 +#define CMD_HDR_FIS_LEN_SHIFT	2 + +/* attribute +*/ +#define CMD_HDR_ATTR_RES	0x00000800 /* Reserved bit, should be 1 */ +#define CMD_HDR_ATTR_VBIST	0x00000400 /* Vendor BIST */ +/* Snoop enable for all descriptor */ +#define CMD_HDR_ATTR_SNOOP	0x00000200 +#define CMD_HDR_ATTR_FPDMA	0x00000100 /* FPDMA queued command */ +#define CMD_HDR_ATTR_RESET	0x00000080 /* Reset - a SRST or device reset */ +/* BIST - require the host to enter BIST mode */ +#define CMD_HDR_ATTR_BIST	0x00000040 +#define CMD_HDR_ATTR_ATAPI	0x00000020 /* ATAPI command */ +#define CMD_HDR_ATTR_TAG	0x0000001f /* TAG mask */ + +#define FLAGS_DMA	0x00000000 +#define FLAGS_FPDMA	0x00000001 + +#define SATA_FLAG_Q_DEP_MASK	0x0000000f +#define SATA_FLAG_WCACHE	0x00000100 +#define SATA_FLAG_FLUSH		0x00000200 +#define SATA_FLAG_FLUSH_EXT	0x00000400 + +#define READ_CMD	0 +#define WRITE_CMD	1 + +extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE]; + +#endif /* __FSL_SATA_H__ */ diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c index f1b1c16b1..661553552 100644 --- a/drivers/gpio/mxc_gpio.c +++ b/drivers/gpio/mxc_gpio.c @@ -41,7 +41,8 @@ static unsigned long gpio_ports[] = {  	[0] = GPIO1_BASE_ADDR,  	[1] = GPIO2_BASE_ADDR,  	[2] = GPIO3_BASE_ADDR, -#if defined(CONFIG_MX51) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q) +#if defined(CONFIG_MX25) || defined(CONFIG_MX51) || defined(CONFIG_MX53) || \ +		defined(CONFIG_MX6Q)  	[3] = GPIO4_BASE_ADDR,  #endif  #if defined(CONFIG_MX53) || defined(CONFIG_MX6Q) diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 21f689726..5b6ea0e75 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -567,3 +567,17 @@ int i2c_set_bus_num(unsigned int bus)  	return 0;  }  #endif + +int tegra_i2c_get_dvc_bus_num(void) +{ +	int i; + +	for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; i++) { +		struct i2c_bus *bus = &i2c_controllers[i]; + +		if (bus->inited && bus->is_dvc) +			return i; +	} + +	return -1; +} diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 1f4dad35b..5c831b261 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -26,10 +26,13 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libinput.o  COBJS-$(CONFIG_I8042_KBD) += i8042.o +COBJS-$(CONFIG_TEGRA2_KEYBOARD) += tegra-kbc.o  ifdef CONFIG_PS2KBD  COBJS-y += keyboard.o pc_keyb.o  COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o  endif +COBJS-y += input.o +COBJS-$(CONFIG_OF_CONTROL) += key_matrix.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/input/input.c b/drivers/input/input.c new file mode 100644 index 000000000..4eadd773b --- /dev/null +++ b/drivers/input/input.c @@ -0,0 +1,430 @@ +/* + * Translate key codes into ASCII + * + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <stdio_dev.h> +#include <input.h> +#include <linux/input.h> + +enum { +	/* These correspond to the lights on the keyboard */ +	FLAG_NUM_LOCK		= 1 << 0, +	FLAG_CAPS_LOCK		= 1 << 1, +	FLAG_SCROLL_LOCK	= 1 << 2, + +	/* Special flag ORed with key code to indicate release */ +	KEY_RELEASE		= 1 << 15, +	KEY_MASK		= 0xfff, +}; + +/* + * These takes map key codes to ASCII. 0xff means no key, or special key. + * Three tables are provided - one for plain keys, one for when the shift + * 'modifier' key is pressed and one for when the ctrl modifier key is + * pressed. + */ +static const uchar kbd_plain_xlate[] = { +	0xff, 0x1b, '1',  '2',  '3',  '4',  '5',  '6', +	'7',  '8',  '9',  '0',  '-',  '=', '\b', '\t',	/* 0x00 - 0x0f */ +	'q',  'w',  'e',  'r',  't',  'y',  'u',  'i', +	'o',  'p',  '[',  ']', '\r', 0xff,  'a',  's',  /* 0x10 - 0x1f */ +	'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';', +	'\'',  '`', 0xff, '\\', 'z',  'x',  'c',  'v',	/* 0x20 - 0x2f */ +	'b',  'n',  'm',  ',' ,  '.', '/', 0xff, 0xff, 0xff, +	' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 0x30 - 0x3f */ +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,  '7', +	'8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',	/* 0x40 - 0x4f */ +	'2',  '3',  '0',  '.', 0xff, 0xff, 0xff, 0xff, +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 0x50 - 0x5F */ +	'\r', 0xff, 0xff +}; + +static unsigned char kbd_shift_xlate[] = { +	0xff, 0x1b, '!', '@', '#', '$', '%', '^', +	'&', '*', '(', ')', '_', '+', '\b', '\t',	/* 0x00 - 0x0f */ +	'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +	'O', 'P', '{', '}', '\r', 0xff, 'A', 'S',	/* 0x10 - 0x1f */ +	'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +	'"', '~', 0xff, '|', 'Z', 'X', 'C', 'V',	/* 0x20 - 0x2f */ +	'B', 'N', 'M', '<', '>', '?', 0xff, 0xff, 0xff, +	' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 0x30 - 0x3f */ +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', +	'8', '9', '-', '4', '5', '6', '+', '1',	/* 0x40 - 0x4f */ +	'2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, 0xff, +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 0x50 - 0x5F */ +	'\r', 0xff, 0xff +}; + +static unsigned char kbd_ctrl_xlate[] = { +	0xff, 0x1b, '1', 0x00, '3', '4', '5', 0x1E, +	'7', '8', '9', '0', 0x1F, '=', '\b', '\t',	/* 0x00 - 0x0f */ +	0x11, 0x17, 0x05, 0x12, 0x14, 0x18, 0x15, 0x09, +	0x0f, 0x10, 0x1b, 0x1d, '\n', 0xff, 0x01, 0x13,	/* 0x10 - 0x1f */ +	0x04, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, ';', +	'\'', '~', 0x00, 0x1c, 0x1a, 0x18, 0x03, 0x16,	/* 0x20 - 0x2f */ +	0x02, 0x0e, 0x0d, '<', '>', '?', 0xff, 0xff, +	0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 0x30 - 0x3f */ +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', +	'8', '9', '-', '4', '5', '6', '+', '1',		/* 0x40 - 0x4f */ +	'2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 0x50 - 0x5F */ +	'\r', 0xff, 0xff +}; + + +int input_queue_ascii(struct input_config *config, int ch) +{ +	if (config->fifo_in + 1 == INPUT_BUFFER_LEN) { +		if (!config->fifo_out) +			return -1; /* buffer full */ +		else +			config->fifo_in = 0; +	} else { +		if (config->fifo_in + 1 == config->fifo_out) +			return -1; /* buffer full */ +		config->fifo_in++; +	} +	config->fifo[config->fifo_in] = (uchar)ch; + +	return 0; +} + +int input_tstc(struct input_config *config) +{ +	if (config->fifo_in == config->fifo_out && config->read_keys) { +		if (!(*config->read_keys)(config)) +			return 0; +	} +	return config->fifo_in != config->fifo_out; +} + +int input_getc(struct input_config *config) +{ +	int err = 0; + +	while (config->fifo_in == config->fifo_out) { +		if (config->read_keys) +			err = (*config->read_keys)(config); +		if (err) +			return -1; +	} + +	if (++config->fifo_out == INPUT_BUFFER_LEN) +		config->fifo_out = 0; + +	return config->fifo[config->fifo_out]; +} + +/** + * Process a modifier/special key press or release and decide which key + * translation array should be used as a result. + * + * TODO: Should keep track of modifier press/release + * + * @param config	Input state + * @param key		Key code to process + * @param release	0 if a press, 1 if a release + * @return pointer to keycode->ascii translation table that should be used + */ +static struct input_key_xlate *process_modifier(struct input_config *config, +						int key, int release) +{ +	struct input_key_xlate *table; +	int flip = -1; +	int i; + +	/* Start with the main table, and see what modifiers change it */ +	assert(config->num_tables > 0); +	table = &config->table[0]; +	for (i = 1; i < config->num_tables; i++) { +		struct input_key_xlate *tab = &config->table[i]; + +		if (key == tab->left_keycode || key == tab->right_keycode) +			table = tab; +	} + +	/* Handle the lighted keys */ +	if (!release) { +		switch (key) { +		case KEY_SCROLLLOCK: +			flip = FLAG_SCROLL_LOCK; +			break; +		case KEY_NUMLOCK: +			flip = FLAG_NUM_LOCK; +			break; +		case KEY_CAPSLOCK: +			flip = FLAG_CAPS_LOCK; +			break; +		} +	} + +	if (flip != -1) { +		int leds = 0; + +		config->leds ^= flip; +		if (config->flags & FLAG_NUM_LOCK) +			leds |= INPUT_LED_NUM; +		if (config->flags & FLAG_CAPS_LOCK) +			leds |= INPUT_LED_CAPS; +		if (config->flags & FLAG_SCROLL_LOCK) +			leds |= INPUT_LED_SCROLL; +		config->leds = leds; +	} + +	return table; +} + +/** + * Search an int array for a key value + * + * @param array	Array to search + * @param count	Number of elements in array + * @param key	Key value to find + * @return element where value was first found, -1 if none + */ +static int array_search(int *array, int count, int key) +{ +	int i; + +	for (i = 0; i < count; i++) { +		if (array[i] == key) +			return i; +	} + +	return -1; +} + +/** + * Sort an array so that those elements that exist in the ordering are + * first in the array, and in the same order as the ordering. The algorithm + * is O(count * ocount) and designed for small arrays. + * + * TODO: Move this to common / lib? + * + * @param dest		Array with elements to sort, also destination array + * @param count		Number of elements to sort + * @param order		Array containing ordering elements + * @param ocount	Number of ordering elements + * @return number of elements in dest that are in order (these will be at the + *	start of dest). + */ +static int sort_array_by_ordering(int *dest, int count, int *order, +				   int ocount) +{ +	int temp[count]; +	int dest_count; +	int same;	/* number of elements which are the same */ +	int i; + +	/* setup output items, copy items to be sorted into our temp area */ +	memcpy(temp, dest, count * sizeof(*dest)); +	dest_count = 0; + +	/* work through the ordering, move over the elements we agree on */ +	for (i = 0; i < ocount; i++) { +		if (array_search(temp, count, order[i]) != -1) +			dest[dest_count++] = order[i]; +	} +	same = dest_count; + +	/* now move over the elements that are not in the ordering */ +	for (i = 0; i < count; i++) { +		if (array_search(order, ocount, temp[i]) == -1) +			dest[dest_count++] = temp[i]; +	} +	assert(dest_count == count); +	return same; +} + +/** + * Check a list of key codes against the previous key scan + * + * Given a list of new key codes, we check how many of these are the same + * as last time. + * + * @param config	Input state + * @param keycode	List of key codes to examine + * @param num_keycodes	Number of key codes + * @param same		Returns number of key codes which are the same + */ +static int input_check_keycodes(struct input_config *config, +			   int keycode[], int num_keycodes, int *same) +{ +	/* Select the 'plain' xlate table to start with */ +	if (!config->num_tables) { +		debug("%s: No xlate tables: cannot decode keys\n", __func__); +		return -1; +	} + +	/* sort the keycodes into the same order as the previous ones */ +	*same = sort_array_by_ordering(keycode, num_keycodes, +			config->prev_keycodes, config->num_prev_keycodes); + +	memcpy(config->prev_keycodes, keycode, num_keycodes * sizeof(int)); +	config->num_prev_keycodes = num_keycodes; + +	return *same != num_keycodes; +} + +/** + * Convert a list of key codes into ASCII + * + * You must call input_check_keycodes() before this. It turns the keycode + * list into a list of ASCII characters which are ready to send to the + * input layer. + * + * Characters which were seen last time do not generate fresh ASCII output. + * + * @param config	Input state + * @param keycode	List of key codes to examine + * @param num_keycodes	Number of key codes + * @param same		Number of key codes which are the same + */ +static int input_keycodes_to_ascii(struct input_config *config, +		int keycode[], int num_keycodes, char output_ch[], int same) +{ +	struct input_key_xlate *table; +	int ch_count; +	int i; + +	table = &config->table[0]; + +	/* deal with modifiers first */ +	for (i = 0; i < num_keycodes; i++) { +		int key = keycode[i] & KEY_MASK; + +		if (key >= table->num_entries || table->xlate[key] == 0xff) { +			table = process_modifier(config, key, +					keycode[i] & KEY_RELEASE); +		} +	} + +	/* now find normal keys */ +	for (i = ch_count = 0; i < num_keycodes; i++) { +		int key = keycode[i]; + +		if (key < table->num_entries && i >= same) { +			int ch = table->xlate[key]; + +			/* If a normal key with an ASCII value, add it! */ +			if (ch != 0xff) +				output_ch[ch_count++] = (uchar)ch; +		} +	} + +	/* ok, so return keys */ +	return ch_count; +} + +int input_send_keycodes(struct input_config *config, +			int keycode[], int num_keycodes) +{ +	char ch[num_keycodes]; +	int count, i, same = 0; +	int is_repeat = 0; +	unsigned delay_ms; + +	config->modifiers = 0; +	if (!input_check_keycodes(config, keycode, num_keycodes, &same)) { +		/* +		 * Same as last time - is it time for another repeat? +		 * TODO(sjg@chromium.org) We drop repeats here and since +		 * the caller may not call in again for a while, our +		 * auto-repeat speed is not quite correct. We should +		 * insert another character if we later realise that we +		 * have missed a repeat slot. +		 */ +		is_repeat = (int)get_timer(config->next_repeat_ms) >= 0; +		if (!is_repeat) +			return 0; +	} + +	count = input_keycodes_to_ascii(config, keycode, num_keycodes, +					ch, is_repeat ? 0 : same); +	for (i = 0; i < count; i++) +		input_queue_ascii(config, ch[i]); +	delay_ms = is_repeat ? +			config->repeat_rate_ms : +			config->repeat_delay_ms; + +	config->next_repeat_ms = get_timer(0) + delay_ms; +	return 0; +} + +int input_add_table(struct input_config *config, int left_keycode, +		    int right_keycode, const uchar *xlate, int num_entries) +{ +	struct input_key_xlate *table; + +	if (config->num_tables == INPUT_MAX_MODIFIERS) { +		debug("%s: Too many modifier tables\n", __func__); +		return -1; +	} + +	table = &config->table[config->num_tables++]; +	table->left_keycode = left_keycode; +	table->right_keycode = right_keycode; +	table->xlate = xlate; +	table->num_entries = num_entries; + +	return 0; +} + +int input_init(struct input_config *config, int leds, int repeat_delay_ms, +	       int repeat_rate_ms) +{ +	memset(config, '\0', sizeof(*config)); +	config->leds = leds; +	config->repeat_delay_ms = repeat_delay_ms; +	config->repeat_rate_ms = repeat_rate_ms; +	if (input_add_table(config, -1, -1, +			kbd_plain_xlate, ARRAY_SIZE(kbd_plain_xlate)) || +		input_add_table(config, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, +			kbd_shift_xlate, ARRAY_SIZE(kbd_shift_xlate)) || +		input_add_table(config, KEY_LEFTCTRL, KEY_RIGHTCTRL, +			kbd_ctrl_xlate, ARRAY_SIZE(kbd_ctrl_xlate))) { +		debug("%s: Could not add modifier tables\n", __func__); +		return -1; +	} + +	return 0; +} + +int input_stdio_register(struct stdio_dev *dev) +{ +	int error; + +	error = stdio_register(dev); + +	/* check if this is the standard input device */ +	if (!error && strcmp(getenv("stdin"), dev->name) == 0) { +		/* reassign the console */ +		if (OVERWRITE_CONSOLE || +				console_assign(stdin, dev->name)) +			return -1; +	} + +	return 0; +} diff --git a/drivers/input/key_matrix.c b/drivers/input/key_matrix.c new file mode 100644 index 000000000..84b898ff3 --- /dev/null +++ b/drivers/input/key_matrix.c @@ -0,0 +1,208 @@ +/* + * Manage Keyboard Matrices + * + * Copyright (c) 2012 The Chromium OS Authors. + * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <fdtdec.h> +#include <key_matrix.h> +#include <malloc.h> +#include <linux/input.h> + +/** + * Determine if the current keypress configuration can cause key ghosting + * + * We figure this out by seeing if we have two or more keys in the same + * column, as well as two or more keys in the same row. + * + * @param config	Keyboard matrix config + * @param keys		List of keys to check + * @param valid		Number of valid keypresses to check + * @return 0 if no ghosting is possible, 1 if it is + */ +static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys, +			int valid) +{ +	int key_in_same_col = 0, key_in_same_row = 0; +	int i, j; + +	for (i = 0; i < valid; i++) { +		/* +		 * Find 2 keys such that one key is in the same row +		 * and the other is in the same column as the i-th key. +		 */ +		for (j = i + 1; j < valid; j++) { +			if (keys[j].col == keys[i].col) +				key_in_same_col = 1; +			if (keys[j].row == keys[i].row) +				key_in_same_row = 1; +		} +	} + +	if (key_in_same_col && key_in_same_row) +		return 1; +	else +		return 0; +} + +int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[], +		      int num_keys, int keycode[], int max_keycodes) +{ +	const u8 *keymap; +	int valid, upto; +	int pos; + +	debug("%s: num_keys = %d\n", __func__, num_keys); +	keymap = config->plain_keycode; +	for (valid = upto = 0; upto < num_keys; upto++) { +		struct key_matrix_key *key = &keys[upto]; + +		debug("  valid=%d, row=%d, col=%d\n", key->valid, key->row, +		      key->col); +		if (!key->valid) +			continue; +		pos = key->row * config->num_cols + key->col; +		if (config->fn_keycode && pos == config->fn_pos) +			keymap = config->fn_keycode; + +		/* Convert the (row, col) values into a keycode */ +		if (valid < max_keycodes) +			keycode[valid++] = keymap[pos]; +		debug("    keycode=%d\n", keymap[pos]); +	} + +	/* For a ghost key config, ignore the keypresses for this iteration. */ +	if (valid >= 3 && has_ghosting(config, keys, valid)) { +		valid = 0; +		debug("    ghosting detected!\n"); +	} +	debug("  %d valid keycodes found\n", valid); + +	return valid; +} + +/** + * Create a new keycode map from some provided data + * + * This decodes a keycode map in the format used by the fdt, which is one + * word per entry, with the row, col and keycode encoded in that word. + * + * We create a (row x col) size byte array with each entry containing the + * keycode for that (row, col). We also search for map_keycode and return + * its position if found (this is used for finding the Fn key). + * + * @param config        Key matrix dimensions structure + * @param data          Keycode data + * @param len           Number of entries in keycode table + * @param map_keycode   Key code to find in the map + * @param pos           Returns position of map_keycode, if found, else -1 + * @return map  Pointer to allocated map + */ +static uchar *create_keymap(struct key_matrix *config, u32 *data, int len, +			    int map_keycode, int *pos) +{ +	uchar *map; + +	if (pos) +		*pos = -1; +	map = (uchar *)calloc(1, config->key_count); +	if (!map) { +		debug("%s: failed to malloc %d bytes\n", __func__, +			config->key_count); +		return NULL; +	} + +	for (; len >= sizeof(u32); data++, len -= 4) { +		u32 tmp = fdt32_to_cpu(*data); +		int key_code, row, col; +		int entry; + +		row = (tmp >> 24) & 0xff; +		col = (tmp >> 16) & 0xff; +		key_code = tmp & 0xffff; +		entry = row * config->num_cols + col; +		map[entry] = key_code; +		if (pos && map_keycode == key_code) +			*pos = entry; +	} + +	return map; +} + +int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, +			  int node) +{ +	const struct fdt_property *prop; +	int offset; + +	/* Check each property name for ones that we understand */ +	for (offset = fdt_first_property_offset(blob, node); +		      offset > 0; +		      offset = fdt_next_property_offset(blob, offset)) { +		const char *name; +		int len; + +		prop = fdt_get_property_by_offset(blob, offset, NULL); +		name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); +		len = strlen(name); + +		/* Name needs to match "1,<type>keymap" */ +		debug("%s: property '%s'\n", __func__, name); +		if (strncmp(name, "1,", 2) || len < 8 || +		    strcmp(name + len - 6, "keymap")) +			continue; + +		len -= 8; +		if (len == 0) { +			config->plain_keycode = create_keymap(config, +				(u32 *)prop->data, fdt32_to_cpu(prop->len), +				KEY_FN, &config->fn_pos); +		} else if (0 == strncmp(name + 2, "fn-", len)) { +			config->fn_keycode = create_keymap(config, +				(u32 *)prop->data, fdt32_to_cpu(prop->len), +				-1, NULL); +		} else { +			debug("%s: unrecognised property '%s'\n", __func__, +			      name); +		} +	} +	debug("%s: Decoded key maps %p, %p from fdt\n", __func__, +	      config->plain_keycode, config->fn_keycode); + +	if (!config->plain_keycode) { +		debug("%s: cannot find keycode-plain map\n", __func__); +		return -1; +	} + +	return 0; +} + +int key_matrix_init(struct key_matrix *config, int rows, int cols) +{ +	memset(config, '\0', sizeof(*config)); +	config->num_rows = rows; +	config->num_cols = cols; +	config->key_count = rows * cols; +	assert(config->key_count > 0); + +	return 0; +} diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c new file mode 100644 index 000000000..f164791be --- /dev/null +++ b/drivers/input/tegra-kbc.c @@ -0,0 +1,375 @@ +/* + *  (C) Copyright 2011 + *  NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <fdtdec.h> +#include <input.h> +#include <key_matrix.h> +#include <stdio_dev.h> +#include <tegra-kbc.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/timer.h> +#include <linux/input.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { +	KBC_MAX_GPIO		= 24, +	KBC_MAX_KPENT		= 8,	/* size of keypress entry queue */ +}; + +#define KBC_FIFO_TH_CNT_SHIFT		14 +#define KBC_DEBOUNCE_CNT_SHIFT		4 +#define KBC_CONTROL_FIFO_CNT_INT_EN	(1 << 3) +#define KBC_CONTROL_KBC_EN		(1 << 0) +#define KBC_INT_FIFO_CNT_INT_STATUS	(1 << 2) +#define KBC_KPENT_VALID			(1 << 7) +#define KBC_ST_STATUS			(1 << 3) + +enum { +	KBC_DEBOUNCE_COUNT	= 2, +	KBC_REPEAT_RATE_MS	= 30, +	KBC_REPEAT_DELAY_MS	= 240, +	KBC_CLOCK_KHZ		= 32,	/* Keyboard uses a 32KHz clock */ +}; + +/* keyboard controller config and state */ +static struct keyb { +	struct input_config input;	/* The input layer */ +	struct key_matrix matrix;	/* The key matrix layer */ + +	struct kbc_tegra *kbc;		/* tegra keyboard controller */ +	unsigned char inited;		/* 1 if keyboard has been inited */ +	unsigned char first_scan;	/* 1 if this is our first key scan */ + +	/* +	 * After init we must wait a short time before polling the keyboard. +	 * This gives the tegra keyboard controller time to react after reset +	 * and lets us grab keys pressed during reset. +	 */ +	unsigned int init_dly_ms;	/* Delay before we can read keyboard */ +	unsigned int start_time_ms;	/* Time that we inited (in ms) */ +	unsigned int last_poll_ms;	/* Time we should last polled */ +	unsigned int next_repeat_ms;	/* Next time we repeat a key */ +} config; + +/** + * reads the keyboard fifo for current keypresses + * + * @param config	Keyboard config + * @param fifo		Place to put fifo results + * @param max_keycodes	Maximum number of key codes to put in the fifo + * @return number of items put into fifo + */ +static int tegra_kbc_find_keys(struct keyb *config, int *fifo, +			       int max_keycodes) +{ +	struct key_matrix_key keys[KBC_MAX_KPENT], *key; +	u32 kp_ent = 0; +	int i; + +	for (key = keys, i = 0; i < KBC_MAX_KPENT; i++, key++) { +		/* Get next word */ +		if (!(i & 3)) +			kp_ent = readl(&config->kbc->kp_ent[i / 4]); + +		key->valid = (kp_ent & KBC_KPENT_VALID) != 0; +		key->row = (kp_ent >> 3) & 0xf; +		key->col = kp_ent & 0x7; + +		/* Shift to get next entry */ +		kp_ent >>= 8; +	} +	return key_matrix_decode(&config->matrix, keys, KBC_MAX_KPENT, fifo, +				 max_keycodes); +} + +/** + * Process all the keypress sequences in fifo and send key codes + * + * The fifo contains zero or more keypress sets. Each set + * consists of from 1-8 keycodes, representing the keycodes which + * were simultaneously pressed during that scan. + * + * This function works through each set and generates ASCII characters + * for each. Not that one set may produce more than one ASCII characters - + * for example holding down 'd' and 'f' at the same time will generate + * two ASCII characters. + * + * Note: if fifo_cnt is 0, we will tell the input layer that no keys are + * pressed. + * + * @param config	Keyboard config + * @param fifo_cnt	Number of entries in the keyboard fifo + */ +static void process_fifo(struct keyb *config, int fifo_cnt) +{ +	int fifo[KBC_MAX_KPENT]; +	int cnt = 0; + +	/* Always call input_send_keycodes() at least once */ +	do { +		if (fifo_cnt) +			cnt = tegra_kbc_find_keys(config, fifo, KBC_MAX_KPENT); + +		input_send_keycodes(&config->input, fifo, cnt); +	} while (--fifo_cnt > 0); +} + +/** + * Check the keyboard controller and emit ASCII characters for any keys that + * are pressed. + * + * @param config	Keyboard config + */ +static void check_for_keys(struct keyb *config) +{ +	int fifo_cnt; + +	if (!config->first_scan && +			get_timer(config->last_poll_ms) < KBC_REPEAT_RATE_MS) +		return; +	config->last_poll_ms = get_timer(0); +	config->first_scan = 0; + +	/* +	 * Once we get here we know the keyboard has been scanned. So if there +	 * scan waiting for us, we know that nothing is held down. +	 */ +	fifo_cnt = (readl(&config->kbc->interrupt) >> 4) & 0xf; +	process_fifo(config, fifo_cnt); +} + +/** + * In order to detect keys pressed on boot, wait for the hardware to + * complete scanning the keys. This includes time to transition from + * Wkup mode to Continous polling mode and the repoll time. We can + * deduct the time that's already elapsed. + * + * @param config	Keyboard config + */ +static void kbd_wait_for_fifo_init(struct keyb *config) +{ +	if (!config->inited) { +		unsigned long elapsed_time; +		long delay_ms; + +		elapsed_time = get_timer(config->start_time_ms); +		delay_ms = config->init_dly_ms - elapsed_time; +		if (delay_ms > 0) { +			udelay(delay_ms * 1000); +			debug("%s: delay %ldms\n", __func__, delay_ms); +		} + +		config->inited = 1; +	} +} + +/** + * Check the tegra keyboard, and send any keys that are pressed. + * + * This is called by input_tstc() and input_getc() when they need more + * characters + * + * @param input		Input configuration + * @return 1, to indicate that we have something to look at + */ +int tegra_kbc_check(struct input_config *input) +{ +	kbd_wait_for_fifo_init(&config); +	check_for_keys(&config); + +	return 1; +} + +/** + * Test if keys are available to be read + * + * @return 0 if no keys available, 1 if keys are available + */ +static int kbd_tstc(void) +{ +	/* Just get input to do this for us */ +	return input_tstc(&config.input); +} + +/** + * Read a key + * + * TODO: U-Boot wants 0 for no key, but Ctrl-@ is a valid key... + * + * @return ASCII key code, or 0 if no key, or -1 if error + */ +static int kbd_getc(void) +{ +	/* Just get input to do this for us */ +	return input_getc(&config.input); +} + +/* configures keyboard GPIO registers to use the rows and columns */ +static void config_kbc_gpio(struct kbc_tegra *kbc) +{ +	int i; + +	for (i = 0; i < KBC_MAX_GPIO; i++) { +		u32 row_cfg, col_cfg; +		u32 r_shift = 5 * (i % 6); +		u32 c_shift = 4 * (i % 8); +		u32 r_mask = 0x1f << r_shift; +		u32 c_mask = 0xf << c_shift; +		u32 r_offs = i / 6; +		u32 c_offs = i / 8; + +		row_cfg = readl(&kbc->row_cfg[r_offs]); +		col_cfg = readl(&kbc->col_cfg[c_offs]); + +		row_cfg &= ~r_mask; +		col_cfg &= ~c_mask; + +		if (i < config.matrix.num_rows) { +			row_cfg |= ((i << 1) | 1) << r_shift; +		} else { +			col_cfg |= (((i - config.matrix.num_rows) << 1) | 1) +					<< c_shift; +		} + +		writel(row_cfg, &kbc->row_cfg[r_offs]); +		writel(col_cfg, &kbc->col_cfg[c_offs]); +	} +} + +/** + * Start up the keyboard device + */ +static void tegra_kbc_open(void) +{ +	struct kbc_tegra *kbc = config.kbc; +	unsigned int scan_period; +	u32 val; + +	/* +	 * We will scan at twice the keyboard repeat rate, so that there is +	 * always a scan ready when we check it in check_for_keys(). +	 */ +	scan_period = KBC_REPEAT_RATE_MS / 2; +	writel(scan_period * KBC_CLOCK_KHZ, &kbc->rpt_dly); +	writel(scan_period * KBC_CLOCK_KHZ, &kbc->init_dly); +	/* +	 * Before reading from the keyboard we must wait for the init_dly +	 * plus the rpt_delay, plus 2ms for the row scan time. +	 */ +	config.init_dly_ms = scan_period * 2 + 2; + +	val = KBC_DEBOUNCE_COUNT << KBC_DEBOUNCE_CNT_SHIFT; +	val |= 1 << KBC_FIFO_TH_CNT_SHIFT;	/* fifo interrupt threshold */ +	val |= KBC_CONTROL_KBC_EN;		/* enable */ +	writel(val, &kbc->control); + +	config.start_time_ms = get_timer(0); +	config.last_poll_ms = config.next_repeat_ms = get_timer(0); +	config.first_scan = 1; +} + +/** + * Set up the tegra keyboard. This is called by the stdio device handler + * + * We want to do this init when the keyboard is actually used rather than + * at start-up, since keyboard input may not currently be selected. + * + * Once the keyboard starts there will be a period during which we must + * wait for the keyboard to init. We do this only when a key is first + * read - see kbd_wait_for_fifo_init(). + * + * @return 0 if ok, -ve on error + */ +static int init_tegra_keyboard(void) +{ +#ifdef CONFIG_OF_CONTROL +	int	node; + +	node = fdtdec_next_compatible(gd->fdt_blob, 0, +					  COMPAT_NVIDIA_TEGRA20_KBC); +	if (node < 0) { +		debug("%s: cannot locate keyboard node\n", __func__); +		return node; +	} +	config.kbc = (struct kbc_tegra *)fdtdec_get_addr(gd->fdt_blob, +		       node, "reg"); +	if ((fdt_addr_t)config.kbc == FDT_ADDR_T_NONE) { +		debug("%s: No keyboard register found\n", __func__); +		return -1; +	} + +	/* Decode the keyboard matrix information (16 rows, 8 columns) */ +	if (key_matrix_init(&config.matrix, 16, 8)) { +		debug("%s: Could not init key matrix\n", __func__); +		return -1; +	} +	if (key_matrix_decode_fdt(&config.matrix, gd->fdt_blob, node)) { +		debug("%s: Could not decode key matrix from fdt\n", __func__); +		return -1; +	} +	if (config.matrix.fn_keycode) { +		if (input_add_table(&config.input, KEY_FN, -1, +				    config.matrix.fn_keycode, +				    config.matrix.key_count)) +			return -1; +	} +#else +#error "Tegra keyboard driver requires FDT definitions" +#endif + +	/* Set up pin mux and enable the clock */ +	funcmux_select(PERIPH_ID_KBC, FUNCMUX_DEFAULT); +	clock_enable(PERIPH_ID_KBC); +	config_kbc_gpio(config.kbc); + +	tegra_kbc_open(); +	debug("%s: Tegra keyboard ready\n", __func__); + +	return 0; +} + +int drv_keyboard_init(void) +{ +	struct stdio_dev dev; + +	if (input_init(&config.input, 0, KBC_REPEAT_DELAY_MS, +			KBC_REPEAT_RATE_MS)) { +		debug("%s: Cannot set up input\n", __func__); +		return -1; +	} +	config.input.read_keys = tegra_kbc_check; + +	memset(&dev, '\0', sizeof(dev)); +	strcpy(dev.name, "tegra-kbc"); +	dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; +	dev.getc = kbd_getc; +	dev.tstc = kbd_tstc; +	dev.start = init_tegra_keyboard; + +	/* Register the device. init_tegra_keyboard() will be called soon */ +	return input_stdio_register(&dev); +} diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a70970784..271463cf1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,10 +35,12 @@ COBJS-$(CONFIG_PDSP188x) += pdsp188x.o  COBJS-$(CONFIG_STATUS_LED) += status_led.o  COBJS-$(CONFIG_TWL4030_LED) += twl4030_led.o  COBJS-$(CONFIG_PMIC) += pmic_core.o +COBJS-$(CONFIG_DIALOG_PMIC) += pmic_dialog.o  COBJS-$(CONFIG_PMIC_FSL) += pmic_fsl.o  COBJS-$(CONFIG_PMIC_I2C) += pmic_i2c.o  COBJS-$(CONFIG_PMIC_SPI) += pmic_spi.o  COBJS-$(CONFIG_PMIC_MAX8998) += pmic_max8998.o +COBJS-$(CONFIG_PMIC_MAX8997) += pmic_max8997.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/misc/pmic_dialog.c b/drivers/misc/pmic_dialog.c new file mode 100644 index 000000000..e97af1d1d --- /dev/null +++ b/drivers/misc/pmic_dialog.c @@ -0,0 +1,37 @@ +/* + *  Copyright (C) 2011 Samsung Electronics + *  Lukasz Majewski <l.majewski@samsung.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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 <common.h> +#include <pmic.h> +#include <dialog_pmic.h> + +int pmic_dialog_init(void) +{ +	struct pmic *p = get_pmic(); +	static const char name[] = "DIALOG_PMIC"; + +	p->name = name; +	p->number_of_regs = DIALOG_NUM_OF_REGS; + +	p->interface = PMIC_I2C; +	p->hw.i2c.addr = CONFIG_SYS_DIALOG_PMIC_I2C_ADDR; +	p->hw.i2c.tx_num = 1; +	p->bus = I2C_PMIC; + +	return 0; +} diff --git a/drivers/misc/pmic_max8997.c b/drivers/misc/pmic_max8997.c new file mode 100644 index 000000000..62dbc0531 --- /dev/null +++ b/drivers/misc/pmic_max8997.c @@ -0,0 +1,43 @@ +/* + *  Copyright (C) 2012 Samsung Electronics + *  Lukasz Majewski <l.majewski@samsung.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <pmic.h> +#include <max8997_pmic.h> + +int pmic_init(void) +{ +	struct pmic *p = get_pmic(); +	static const char name[] = "MAX8997_PMIC"; + +	puts("Board PMIC init\n"); + +	p->name = name; +	p->interface = PMIC_I2C; +	p->number_of_regs = PMIC_NUM_OF_REGS; +	p->hw.i2c.addr = MAX8997_I2C_ADDR; +	p->hw.i2c.tx_num = 1; +	p->bus = I2C_PMIC; + +	return 0; +} diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 6572e9551..4187a9412 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -133,7 +133,8 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  		/* READ or WRITE */  		if (data->flags & MMC_DATA_READ) {  			ctrl0 |= SSP_CTRL0_READ; -		} else if (priv->mmc_is_wp(mmc->block_dev.dev)) { +		} else if (priv->mmc_is_wp && +			priv->mmc_is_wp(mmc->block_dev.dev)) {  			printf("MMC%d: Can not write a locked card!\n",  				mmc->block_dev.dev);  			return UNUSABLE_ERR; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index f2a7a7871..afd9b30b5 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -29,6 +29,7 @@  #include <i2c.h>  #include <twl4030.h>  #include <twl6030.h> +#include <twl6035.h>  #include <asm/io.h>  #include <asm/arch/mmc_host_def.h>  #include <asm/arch/sys_proto.h> @@ -49,8 +50,8 @@ static struct mmc hsmmc_dev[2];  static void omap4_vmmc_pbias_config(struct mmc *mmc)  {  	u32 value = 0; -	struct omap4_sys_ctrl_regs *const ctrl = -		(struct omap4_sys_ctrl_regs *)SYSCTRL_GENERAL_CORE_BASE; +	struct omap_sys_ctrl_regs *const ctrl = +		(struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE;  	value = readl(&ctrl->control_pbiaslite); @@ -64,6 +65,34 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc)  }  #endif +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +static void omap5_pbias_config(struct mmc *mmc) +{ +	u32 value = 0; +	struct omap_sys_ctrl_regs *const ctrl = +		(struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE; + +	value = readl(&ctrl->control_pbias); +	value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); +	value |= SDCARD_BIAS_HIZ_MODE; +	writel(value, &ctrl->control_pbias); + +	twl6035_mmc1_poweron_ldo(); + +	value = readl(&ctrl->control_pbias); +	value &= ~SDCARD_BIAS_HIZ_MODE; +	value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ; +	writel(value, &ctrl->control_pbias); + +	value = readl(&ctrl->control_pbias); +	if (value & (1 << 23)) { +		value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); +		value |= SDCARD_BIAS_HIZ_MODE; +		writel(value, &ctrl->control_pbias); +	} +} +#endif +  unsigned char mmc_board_init(struct mmc *mmc)  {  #if defined(CONFIG_OMAP34XX) @@ -90,6 +119,11 @@ unsigned char mmc_board_init(struct mmc *mmc)  	writel(readl(&t2_base->devconf1) | MMCSDIO2ADPCLKISEL,  		&t2_base->devconf1); +	/* Change from default of 52MHz to 26MHz if necessary */ +	if (!(mmc->host_caps & MMC_MODE_HS_52MHz)) +		writel(readl(&t2_base->ctl_prog_io1) & ~CTLPROGIO1SPEEDCTRL, +			&t2_base->ctl_prog_io1); +  	writel(readl(&prcm_base->fclken1_core) |  		EN_MMC1 | EN_MMC2 | EN_MMC3,  		&prcm_base->fclken1_core); @@ -104,6 +138,10 @@ unsigned char mmc_board_init(struct mmc *mmc)  	if (mmc->block_dev.dev == 0)  		omap4_vmmc_pbias_config(mmc);  #endif +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +	if (mmc->block_dev.dev == 0) +		omap5_pbias_config(mmc); +#endif  	return 0;  } @@ -502,7 +540,7 @@ static void mmc_set_ios(struct mmc *mmc)  	writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);  } -int omap_mmc_init(int dev_index) +int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max)  {  	struct mmc *mmc; @@ -533,11 +571,22 @@ int omap_mmc_init(int dev_index)  		return 1;  	}  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; -	mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | -				MMC_MODE_HC; +	mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | +				MMC_MODE_HC) & ~host_caps_mask;  	mmc->f_min = 400000; -	mmc->f_max = 52000000; + +	if (f_max != 0) +		mmc->f_max = f_max; +	else { +		if (mmc->host_caps & MMC_MODE_HS) { +			if (mmc->host_caps & MMC_MODE_HS_52MHz) +				mmc->f_max = 52000000; +			else +				mmc->f_max = 26000000; +		} else +			mmc->f_max = 20000000; +	}  	mmc->b_max = 0; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 35e89a0f4..936186f75 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1302,12 +1302,45 @@ static void mxc_setup_config1(void)  #define mxc_setup_config1()  #endif +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { +	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | +		   NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, +	.offs =	0, +	.len = 4, +	.veroffs = 4, +	.maxblocks = 4, +	.pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { +	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | +		   NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, +	.offs =	0, +	.len = 4, +	.veroffs = 4, +	.maxblocks = 4, +	.pattern = mirror_pattern, +}; + +#endif +  int board_nand_init(struct nand_chip *this)  {  	struct mtd_info *mtd;  	uint16_t tmp;  	int err = 0; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT +	this->options |= NAND_USE_FLASH_BBT; +	this->bbt_td = &bbt_main_descr; +	this->bbt_md = &bbt_mirror_descr; +#endif +  	/* structures must be linked */  	mtd = &host->mtd;  	mtd->priv = this; diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index d8db9f0c6..156fa8f91 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -187,9 +187,10 @@ int fec_phy_write(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr,  #ifndef CONFIG_PHYLIB  static int miiphy_restart_aneg(struct eth_device *dev)  { +	int ret = 0; +#if !defined(CONFIG_FEC_MXC_NO_ANEG)  	struct fec_priv *fec = (struct fec_priv *)dev->priv;  	struct ethernet_regs *eth = fec->bus->priv; -	int ret = 0;  	/*  	 * Wake up from sleep if necessary @@ -213,6 +214,7 @@ static int miiphy_restart_aneg(struct eth_device *dev)  	if (fec->mii_postcall)  		ret = fec->mii_postcall(fec->phy_id); +#endif  	return ret;  } @@ -398,6 +400,42 @@ static void fec_eth_phy_config(struct eth_device *dev)  #endif  } +/* + * Do initial configuration of the FEC registers + */ +static void fec_reg_setup(struct fec_priv *fec) +{ +	uint32_t rcntrl; + +	/* +	 * Set interrupt mask register +	 */ +	writel(0x00000000, &fec->eth->imask); + +	/* +	 * Clear FEC-Lite interrupt event register(IEVENT) +	 */ +	writel(0xffffffff, &fec->eth->ievent); + + +	/* +	 * Set FEC-Lite receive control register(R_CNTRL): +	 */ + +	/* Start with frame length = 1518, common for all modes. */ +	rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT; +	if (fec->xcv_type == SEVENWIRE) +		rcntrl |= FEC_RCNTRL_FCE; +	else if (fec->xcv_type == RGMII) +		rcntrl |= FEC_RCNTRL_RGMII; +	else if (fec->xcv_type == RMII) +		rcntrl |= FEC_RCNTRL_RMII; +	else	/* MII mode */ +		rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE; + +	writel(rcntrl, &fec->eth->r_cntrl); +} +  /**   * Start the FEC engine   * @param[in] dev Our device to handle @@ -512,7 +550,6 @@ static int fec_init(struct eth_device *dev, bd_t* bd)  {  	struct fec_priv *fec = (struct fec_priv *)dev->priv;  	uint32_t mib_ptr = (uint32_t)&fec->eth->rmon_t_drop; -	uint32_t rcntrl;  	uint32_t size;  	int i, ret; @@ -560,33 +597,7 @@ static int fec_init(struct eth_device *dev, bd_t* bd)  				   (unsigned)fec->rbd_base + size);  	} -	/* -	 * Set interrupt mask register -	 */ -	writel(0x00000000, &fec->eth->imask); - -	/* -	 * Clear FEC-Lite interrupt event register(IEVENT) -	 */ -	writel(0xffffffff, &fec->eth->ievent); - - -	/* -	 * Set FEC-Lite receive control register(R_CNTRL): -	 */ - -	/* Start with frame length = 1518, common for all modes. */ -	rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT; -	if (fec->xcv_type == SEVENWIRE) -		rcntrl |= FEC_RCNTRL_FCE; -	else if (fec->xcv_type == RGMII) -		rcntrl |= FEC_RCNTRL_RGMII; -	else if (fec->xcv_type == RMII) -		rcntrl |= FEC_RCNTRL_RMII; -	else	/* MII mode */ -		rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE; - -	writel(rcntrl, &fec->eth->r_cntrl); +	fec_reg_setup(fec);  	if (fec->xcv_type == MII10 || fec->xcv_type == MII100)  		fec_mii_setspeed(fec); @@ -933,24 +944,7 @@ static int fec_probe(bd_t *bd, int dev_id, int phy_id, uint32_t base_addr)  		udelay(10);  	} -	/* -	 * Set interrupt mask register -	 */ -	writel(0x00000000, &fec->eth->imask); - -	/* -	 * Clear FEC-Lite interrupt event register(IEVENT) -	 */ -	writel(0xffffffff, &fec->eth->ievent); - -	/* -	 * Set FEC-Lite receive control register(R_CNTRL): -	 */ -	/* -	 * Frame length=1518; MII mode; -	 */ -	writel((PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT) | FEC_RCNTRL_FCE | -		FEC_RCNTRL_MII_MODE, &fec->eth->r_cntrl); +	fec_reg_setup(fec);  	fec_mii_setspeed(fec);  	if (dev_id == -1) { diff --git a/drivers/power/Makefile b/drivers/power/Makefile index ead00f8da..6bf388cb7 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -26,8 +26,10 @@ include $(TOPDIR)/config.mk  LIB 	:= $(obj)libpower.o  COBJS-$(CONFIG_FTPMU010_POWER)	+= ftpmu010.o +COBJS-$(CONFIG_TPS6586X_POWER)	+= tps6586x.o  COBJS-$(CONFIG_TWL4030_POWER)	+= twl4030.o  COBJS-$(CONFIG_TWL6030_POWER)	+= twl6030.o +COBJS-$(CONFIG_TWL6035_POWER)	+= twl6035.o  COBJS	:= $(COBJS-y)  SRCS 	:= $(COBJS:.o=.c) diff --git a/drivers/power/tps6586x.c b/drivers/power/tps6586x.c new file mode 100644 index 000000000..f3f2ec6e5 --- /dev/null +++ b/drivers/power/tps6586x.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <tps6586x.h> +#include <asm/io.h> +#include <i2c.h> + +static int bus_num;		/* I2C bus we are on */ +#define I2C_ADDRESS		0x34	/* chip requires this address */ +static char inited;		/* 1 if we have been inited */ + +enum { +	/* Registers that we access */ +	SUPPLY_CONTROL1		= 0x20, +	SUPPLY_CONTROL2, +	SM1_VOLTAGE_V1		= 0x23, +	SM1_VOLTAGE_V2, +	SM0_VOLTAGE_V1		= 0x26, +	SM0_VOLTAGE_V2, +	PFM_MODE		= 0x47, + +	/* Bits in the supply control registers */ +	CTRL_SM1_RAMP		= 0x01, +	CTRL_SM1_SUPPLY2	= 0x02, +	CTRL_SM0_RAMP		= 0x04, +	CTRL_SM0_SUPPLY2	= 0x08, +}; + +#define MAX_I2C_RETRY	3 +int tps6586x_read(int reg) +{ +	int	i; +	uchar	data; +	int	retval = -1; +	int	old_bus_num; + +	old_bus_num = i2c_get_bus_num(); +	i2c_set_bus_num(bus_num); + +	for (i = 0; i < MAX_I2C_RETRY; ++i) { +		if (!i2c_read(I2C_ADDRESS, reg, 1, &data, 1)) { +			retval = (int)data; +			goto exit; +		} + +		/* i2c access failed, retry */ +		udelay(100); +	} + +exit: +	i2c_set_bus_num(old_bus_num); +	debug("pmu_read %x=%x\n", reg, retval); +	if (retval < 0) +		debug("%s: failed to read register %#x: %d\n", __func__, reg, +		      retval); +	return retval; +} + +int tps6586x_write(int reg, uchar *data, uint len) +{ +	int	i; +	int	retval = -1; +	int	old_bus_num; + +	old_bus_num = i2c_get_bus_num(); +	i2c_set_bus_num(bus_num); + +	for (i = 0; i < MAX_I2C_RETRY; ++i) { +		if (!i2c_write(I2C_ADDRESS, reg, 1, data, len)) { +			retval = 0; +			goto exit; +		} + +		/* i2c access failed, retry */ +		udelay(100); +	} + +exit: +	i2c_set_bus_num(old_bus_num); +	debug("pmu_write %x=%x: ", reg, retval); +	for (i = 0; i < len; i++) +		debug("%x ", data[i]); +	if (retval) +		debug("%s: failed to write register %#x\n", __func__, reg); +	return retval; +} + +/* + * Get current voltage of SM0 and SM1 + * + * @param sm0	Place to put SM0 voltage + * @param sm1	Place to put SM1 voltage + * @return 0 if ok, -1 on error + */ +static int read_voltages(int *sm0, int *sm1) +{ +	int ctrl1, ctrl2; +	int is_v2; + +	/* +	 * Each vdd has two supply sources, ie, v1 and v2. +	 * The supply control reg1 and reg2 determine the current selection. +	 */ +	ctrl1 = tps6586x_read(SUPPLY_CONTROL1); +	ctrl2 = tps6586x_read(SUPPLY_CONTROL2); +	if (ctrl1 == -1 || ctrl2 == -1) +		return -1; + +	/* Figure out whether V1 or V2 is selected */ +	is_v2 = (ctrl1 | ctrl2) & CTRL_SM0_SUPPLY2; +	*sm0 = tps6586x_read(is_v2 ? SM0_VOLTAGE_V2 : SM0_VOLTAGE_V1); +	*sm1 = tps6586x_read(is_v2 ? SM1_VOLTAGE_V2 : SM1_VOLTAGE_V1); +	if (*sm0 == -1 || *sm1 == -1) +		return -1; + +	return 0; +} + +static int set_voltage(int reg, int data, int rate) +{ +	uchar control_bit; +	uchar buff[3]; + +	control_bit = (reg == SM0_VOLTAGE_V1 ? CTRL_SM0_RAMP : CTRL_SM1_RAMP); + +	/* +	 * Only one supply is needed in u-boot. set both v1 and v2 to +	 * same value. +	 * +	 * When both v1 and v2 are set to same value, we just need to set +	 * control1 reg to trigger the supply selection. +	 */ +	buff[0] = buff[1] = (uchar)data; +	buff[2] = rate; + +	/* write v1, v2 and rate, then trigger */ +	if (tps6586x_write(reg, buff, 3) || +	    tps6586x_write(SUPPLY_CONTROL1, &control_bit, 1)) +		return -1; + +	return 0; +} + +static int calculate_next_voltage(int voltage, int target, int step) +{ +	int diff = voltage < target ? step : -step; + +	if (abs(target - voltage) > step) +		voltage += diff; +	else +		voltage = target; + +	return voltage; +} + +int tps6586x_set_pwm_mode(int mask) +{ +	uchar val; +	int ret; + +	assert(inited); +	ret = tps6586x_read(PFM_MODE); +	if (ret != -1) { +		val = (uchar)ret; +		val |= mask; + +		ret = tps6586x_write(PFM_MODE, &val, 1); +	} + +	if (ret == -1) +		debug("%s: Failed to read/write PWM mode reg\n", __func__); + +	return ret; +} + +int tps6586x_adjust_sm0_sm1(int sm0_target, int sm1_target, int step, int rate, +			    int min_sm0_over_sm1) +{ +	int sm0, sm1; +	int bad; + +	assert(inited); + +	/* get current voltage settings */ +	if (read_voltages(&sm0, &sm1)) { +		debug("%s: Cannot read voltage settings\n", __func__); +		return -1; +	} + +	/* +	 * if vdd_core < vdd_cpu + rel +	 *    skip +	 * +	 * This condition may happen when system reboots due to kernel crash. +	 */ +	if (min_sm0_over_sm1 != -1 && sm0 < sm1 + min_sm0_over_sm1) { +		debug("%s: SM0 is %d, SM1 is %d, but min_sm0_over_sm1 is %d\n", +		      __func__, sm0, sm1, min_sm0_over_sm1); +		return -1; +	} + +	/* +	 * Since vdd_core and vdd_cpu may both stand at either greater or less +	 * than their nominal voltage, the adjustment may go either directions. +	 * +	 * Make sure vdd_core is always higher than vdd_cpu with certain margin. +	 * So, find out which vdd to adjust first in each step. +	 * +	 * case 1: both sm0 and sm1 need to move up +	 *              adjust sm0 before sm1 +	 * +	 * case 2: both sm0 and sm1 need to move down +	 *              adjust sm1 before sm0 +	 * +	 * case 3: sm0 moves down and sm1 moves up +	 *              adjusting either one first is fine. +	 * +	 * Adjust vdd_core and vdd_cpu one step at a time until they reach +	 * their nominal values. +	 */ +	bad = 0; +	while (!bad && (sm0 != sm0_target || sm1 != sm1_target)) { +		int adjust_sm0_late = 0; /* flag to adjust vdd_core later */ + +		debug("%d-%d   %d-%d   ", sm0, sm0_target, sm1, sm1_target); + +		if (sm0 != sm0_target) { +			/* +			 * if case 1 and case 3, set new sm0 first. +			 * otherwise, hold down until new sm1 is set. +			 */ +			sm0 = calculate_next_voltage(sm0, sm0_target, step); +			if (sm1 < sm1_target) +				bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate); +			else +				adjust_sm0_late = 1; +		} + +		if (sm1 != sm1_target) { +			sm1 = calculate_next_voltage(sm1, sm1_target, step); +			bad |= set_voltage(SM1_VOLTAGE_V1, sm1, rate); +		} + +		if (adjust_sm0_late) +			bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate); +		debug("%d\n", adjust_sm0_late); +	} +	debug("%d-%d   %d-%d   done\n", sm0, sm0_target, sm1, sm1_target); + +	return bad ? -1 : 0; +} + +int tps6586x_init(int bus) +{ +	bus_num = bus; +	inited = 1; + +	return 0; +} diff --git a/drivers/power/twl6035.c b/drivers/power/twl6035.c new file mode 100644 index 000000000..624c09e85 --- /dev/null +++ b/drivers/power/twl6035.c @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2012 + * Texas Instruments, <www.ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <config.h> +#include <twl6035.h> + +/* Functions to read and write from TWL6030 */ +int twl6035_i2c_write_u8(u8 chip_no, u8 val, u8 reg) +{ +	return i2c_write(chip_no, reg, 1, &val, 1); +} + +int twl6035_i2c_read_u8(u8 chip_no, u8 *val, u8 reg) +{ +	return i2c_read(chip_no, reg, 1, val, 1); +} + +/* To align with i2c mw/mr address, reg, val command syntax */ +static inline int palmas_write_u8(u8 chip_no, u8 reg, u8 val) +{ +	return i2c_write(chip_no, reg, 1, &val, 1); +} + +static inline int palmas_read_u8(u8 chip_no, u8 reg, u8 *val) +{ +	return i2c_read(chip_no, reg, 1, val, 1); +} + +void twl6035_init_settings(void) +{ +	return; +} + +void twl6035_mmc1_poweron_ldo(void) +{ +	u8 val = 0; + +	/* set LDO9 TWL6035 to 3V */ +	val = 0x2b; /* (3 -.9)*28 +1 */ +	palmas_write_u8(0x48, LDO9_VOLTAGE, val); + +	/* TURN ON LDO9 */ +	val = LDO_ON | LDO_MODE_SLEEP | LDO_MODE_ACTIVE; +	palmas_write_u8(0x48, LDO9_CTRL, val); +	return; +} diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 616b85703..65d0f234e 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -29,6 +29,7 @@ COBJS-$(CONFIG_ALTERA_UART) += altera_uart.o  COBJS-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o  COBJS-$(CONFIG_ARM_DCC) += arm_dcc.o  COBJS-$(CONFIG_ATMEL_USART) += atmel_usart.o +COBJS-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o  COBJS-$(CONFIG_MCFUART) += mcfuart.o  COBJS-$(CONFIG_NS9750_UART) += ns9750_serial.o  COBJS-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o diff --git a/drivers/serial/lpc32xx_hsuart.c b/drivers/serial/lpc32xx_hsuart.c new file mode 100644 index 000000000..8ce3382d8 --- /dev/null +++ b/drivers/serial/lpc32xx_hsuart.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 Vladimir Zapolskiy <vz@mleia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clk.h> +#include <asm/arch/uart.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct hsuart_regs *hsuart = (struct hsuart_regs *)HS_UART_BASE; + +static void lpc32xx_hsuart_set_baudrate(void) +{ +	u32 div; + +	/* UART rate = PERIPH_CLK / ((HSU_RATE + 1) x 14) */ +	div = (get_serial_clock() / 14 + gd->baudrate / 2) / gd->baudrate - 1; +	if (div > 255) +		div = 255; + +	writel(div, &hsuart->rate); +} + +static int lpc32xx_hsuart_getc(void) +{ +	while (!(readl(&hsuart->level) & HSUART_LEVEL_RX)) +		/* NOP */; + +	return readl(&hsuart->rx) & HSUART_RX_DATA; +} + +static void lpc32xx_hsuart_putc(const char c) +{ +	writel(c, &hsuart->tx); + +	/* Wait for character to be sent */ +	while (readl(&hsuart->level) & HSUART_LEVEL_TX) +		/* NOP */; +} + +static int lpc32xx_hsuart_tstc(void) +{ +	if (readl(&hsuart->level) & HSUART_LEVEL_RX) +		return 1; + +	return 0; +} + +static void lpc32xx_hsuart_init(void) +{ +	lpc32xx_hsuart_set_baudrate(); + +	/* Disable hardware RTS and CTS flow control, set up RX and TX FIFO */ +	writel(HSUART_CTRL_TMO_16 | HSUART_CTRL_HSU_OFFSET(20) | +	       HSUART_CTRL_HSU_RX_TRIG_32 | HSUART_CTRL_HSU_TX_TRIG_0, +	       &hsuart->ctrl); +} + +void serial_setbrg(void) +{ +	return lpc32xx_hsuart_set_baudrate(); +} + +void serial_putc(const char c) +{ +	lpc32xx_hsuart_putc(c); + +	/* If \n, also do \r */ +	if (c == '\n') +		lpc32xx_hsuart_putc('\r'); +} + +int serial_getc(void) +{ +	return lpc32xx_hsuart_getc(); +} + +void serial_puts(const char *s) +{ +	while (*s) +		serial_putc(*s++); +} + +int serial_tstc(void) +{ +	return lpc32xx_hsuart_tstc(); +} + +int serial_init(void) +{ +	lpc32xx_hsuart_init(); + +	return 0; +} diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 4e6f14ee0..7859536a6 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -34,6 +34,8 @@  #define	MXS_SPI_MAX_TIMEOUT	1000000  #define	MXS_SPI_PORT_OFFSET	0x2000 +#define MXS_SSP_CHIPSELECT_MASK		0x00300000 +#define MXS_SSP_CHIPSELECT_SHIFT	20  struct mxs_spi_slave {  	struct spi_slave	slave; @@ -51,14 +53,25 @@ void spi_init(void)  {  } +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ +	/* MXS SPI: 4 ports and 3 chip selects maximum */ +	if (bus > 3 || cs > 2) +		return 0; +	else +		return 1; +} +  struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  				  unsigned int max_hz, unsigned int mode)  {  	struct mxs_spi_slave *mxs_slave;  	uint32_t addr; +	struct mx28_ssp_regs *ssp_regs; +	int reg; -	if (bus > 3) { -		printf("MXS SPI: Max bus number is 3\n"); +	if (!spi_cs_is_valid(bus, cs)) { +		printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);  		return NULL;  	} @@ -73,7 +86,13 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	mxs_slave->max_khz = max_hz / 1000;  	mxs_slave->mode = mode;  	mxs_slave->regs = (struct mx28_ssp_regs *)addr; +	ssp_regs = mxs_slave->regs; + +	reg = readl(&ssp_regs->hw_ssp_ctrl0); +	reg &= ~(MXS_SSP_CHIPSELECT_MASK); +	reg |= cs << MXS_SSP_CHIPSELECT_SHIFT; +	writel(reg, &ssp_regs->hw_ssp_ctrl0);  	return &mxs_slave->slave;  } diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 5dec673c8..42c77fe33 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -73,7 +73,8 @@ static void usbh1_internal_phy_clock_gate(int on)  static void usbh1_power_config(void)  { -	struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; +	struct anatop_regs __iomem *anatop = +		(struct anatop_regs __iomem *)ANATOP_BASE_ADDR;  	/*  	 * Some phy and power's special controls for host1  	 * 1. The external charger detector needs to be disabled @@ -87,7 +88,7 @@ static void usbh1_power_config(void)  		     &anatop->usb2_chrg_detect);  	__raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS, -		     &anatop->usb2_pll_480_ctrl); +		     &anatop->usb2_pll_480_ctrl_clr);  	__raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE |  		     ANADIG_USB2_PLL_480_CTRL_POWER | diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 61dbccd53..45cbd18a0 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -125,11 +125,7 @@ int ehci_hcd_init(void)  	hcor = (struct ehci_hcor *)((uint32_t) hccr +  			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));  	setbits_le32(&ehci->usbmode, CM_HOST); -#ifdef CONFIG_MX31 -	setbits_le32(&ehci->control, USB_EN); -  	__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); -#endif  	mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);  	udelay(10000); diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6252f6a25..4fad20dd6 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -28,7 +28,11 @@ LIB	:= $(obj)libvideo.o  COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o  COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o  COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o +COBJS-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o +COBJS-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \ +				exynos_mipi_dsi_lowlevel.o  COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o +COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o  COBJS-$(CONFIG_S6E63D6) += s6e63d6.o  COBJS-$(CONFIG_SED156X) += sed156x.o  COBJS-$(CONFIG_VIDEO_AMBA) += amba.o diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c new file mode 100644 index 000000000..a1cf44964 --- /dev/null +++ b/drivers/video/exynos_fb.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <lcd.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clock.h> +#include <asm/arch/clk.h> +#include <asm/arch/mipi_dsim.h> +#include <asm/arch/system.h> + +#include "exynos_fb.h" + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +void *lcd_base; +void *lcd_console_address; + +short console_col; +short console_row; + +static unsigned int panel_width, panel_height; + +/* LCD Panel data */ +vidinfo_t panel_info; + +static void exynos_lcd_init_mem(void *lcdbase, vidinfo_t *vid) +{ +	unsigned long palette_size; +	unsigned int fb_size; + +	fb_size = vid->vl_row * vid->vl_col * (NBITS(vid->vl_bpix) >> 3); + +	lcd_base = lcdbase; + +	palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16; + +	exynos_fimd_lcd_init_mem((unsigned long)lcd_base, +			(unsigned long)fb_size, palette_size); +} + +static void exynos_lcd_init(vidinfo_t *vid) +{ +	exynos_fimd_lcd_init(vid); +} + +static void lcd_panel_on(vidinfo_t *vid) +{ +	udelay(vid->init_delay); + +	if (vid->backlight_reset) +		vid->backlight_reset(); + +	if (vid->cfg_gpio) +		vid->cfg_gpio(); + +	if (vid->lcd_power_on) +		vid->lcd_power_on(); + +	udelay(vid->power_on_delay); + +	if (vid->reset_lcd) { +		vid->reset_lcd(); +		udelay(vid->reset_delay); +	} + +	if (vid->backlight_on) +		vid->backlight_on(1); + +	if (vid->cfg_ldo) +		vid->cfg_ldo(); + +	if (vid->enable_ldo) +		vid->enable_ldo(1); + +	if (vid->mipi_enabled) +		exynos_mipi_dsi_init(); +} + +void lcd_ctrl_init(void *lcdbase) +{ +	set_system_display_ctrl(); +	set_lcd_clk(); + +	/* initialize parameters which is specific to panel. */ +	init_panel_info(&panel_info); + +	panel_width = panel_info.vl_width; +	panel_height = panel_info.vl_height; + +	exynos_lcd_init_mem(lcdbase, &panel_info); + +	exynos_lcd_init(&panel_info); +} + +void lcd_enable(void) +{ +	lcd_panel_on(&panel_info); +} + +/* dummy function */ +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +	return; +} diff --git a/drivers/video/exynos_fb.h b/drivers/video/exynos_fb.h new file mode 100644 index 000000000..66f5da6d4 --- /dev/null +++ b/drivers/video/exynos_fb.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _EXYNOS_FB_H_ +#define _EXYNOS_FB_H_ + +#include <asm/arch/fb.h> + +#define MAX_CLOCK	(86 * 1000000) + +enum exynos_fb_rgb_mode_t { +	MODE_RGB_P = 0, +	MODE_BGR_P = 1, +	MODE_RGB_S = 2, +	MODE_BGR_S = 3, +}; + +enum exynos_cpu_auto_cmd_rate { +	DISABLE_AUTO_FRM, +	PER_TWO_FRM, +	PER_FOUR_FRM, +	PER_SIX_FRM, +	PER_EIGHT_FRM, +	PER_TEN_FRM, +	PER_TWELVE_FRM, +	PER_FOURTEEN_FRM, +	PER_SIXTEEN_FRM, +	PER_EIGHTEEN_FRM, +	PER_TWENTY_FRM, +	PER_TWENTY_TWO_FRM, +	PER_TWENTY_FOUR_FRM, +	PER_TWENTY_SIX_FRM, +	PER_TWENTY_EIGHT_FRM, +	PER_THIRTY_FRM, +}; + +void exynos_fimd_lcd_init_mem(unsigned long screen_base, unsigned long fb_size, +	unsigned long palette_size); +void exynos_fimd_lcd_init(vidinfo_t *vid); +unsigned long exynos_fimd_calc_fbsize(void); + +#endif diff --git a/drivers/video/exynos_fimd.c b/drivers/video/exynos_fimd.c new file mode 100644 index 000000000..6416b90fc --- /dev/null +++ b/drivers/video/exynos_fimd.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> +#include <lcd.h> +#include <div64.h> +#include <asm/arch/clk.h> +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> +#include "exynos_fb.h" + +static unsigned long *lcd_base_addr; +static vidinfo_t *pvid; + +void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, +		u_long palette_size) +{ +	lcd_base_addr = (unsigned long *)screen_base; +} + +static void exynos_fimd_set_dualrgb(unsigned int enabled) +{ +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); +	unsigned int cfg = 0; + +	if (enabled) { +		cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT | +			EXYNOS_DUALRGB_VDEN_EN_ENABLE; + +		/* in case of Line Split mode, MAIN_CNT doesn't neet to set. */ +		cfg |= EXYNOS_DUALRGB_SUB_CNT(pvid->vl_col / 2) | +			EXYNOS_DUALRGB_MAIN_CNT(0); +	} + +	writel(cfg, &fimd_ctrl->dualrgb); +} + +static void exynos_fimd_set_par(unsigned int win_id) +{ +	unsigned int cfg = 0; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	/* set window control */ +	cfg = readl((unsigned int)&fimd_ctrl->wincon0 + +			EXYNOS_WINCON(win_id)); + +	cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE | +		EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE | +		EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK | +		EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK); + +	/* DATAPATH is DMA */ +	cfg |= EXYNOS_WINCON_DATAPATH_DMA; + +	/* bpp is 32 */ +	cfg |= EXYNOS_WINCON_WSWP_ENABLE; + +	/* dma burst is 16 */ +	cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; + +	/* pixel format is unpacked RGB888 */ +	cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888; + +	writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + +			EXYNOS_WINCON(win_id)); + +	/* set window position to x=0, y=0*/ +	cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0); +	writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a + +			EXYNOS_VIDOSD(win_id)); + +	cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) | +		EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1); +	writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b + +			EXYNOS_VIDOSD(win_id)); + +	/* set window size for window0*/ +	cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row); +	writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c + +			EXYNOS_VIDOSD(win_id)); +} + +static void exynos_fimd_set_buffer_address(unsigned int win_id) +{ +	unsigned long start_addr, end_addr; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	start_addr = (unsigned long)lcd_base_addr; +	end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) * +				pvid->vl_row); + +	writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 + +			EXYNOS_BUFFER_OFFSET(win_id)); +	writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 + +			EXYNOS_BUFFER_OFFSET(win_id)); +} + +static void exynos_fimd_set_clock(vidinfo_t *pvid) +{ +	unsigned int cfg = 0, div = 0, remainder, remainder_div; +	unsigned long pixel_clock; +	unsigned long long src_clock; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	if (pvid->dual_lcd_enabled) { +		pixel_clock = pvid->vl_freq * +				(pvid->vl_hspw + pvid->vl_hfpd + +				 pvid->vl_hbpd + pvid->vl_col / 2) * +				(pvid->vl_vspw + pvid->vl_vfpd + +				 pvid->vl_vbpd + pvid->vl_row); +	} else if (pvid->interface_mode == FIMD_CPU_INTERFACE) { +		pixel_clock = pvid->vl_freq * +				pvid->vl_width * pvid->vl_height * +				(pvid->cs_setup + pvid->wr_setup + +				 pvid->wr_act + pvid->wr_hold + 1); +	} else { +		pixel_clock = pvid->vl_freq * +				(pvid->vl_hspw + pvid->vl_hfpd + +				 pvid->vl_hbpd + pvid->vl_col) * +				(pvid->vl_vspw + pvid->vl_vfpd + +				 pvid->vl_vbpd + pvid->vl_row); +	} + +	cfg = readl(&fimd_ctrl->vidcon0); +	cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK | +		EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK | +		EXYNOS_VIDCON0_CLKDIR_MASK); +	cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS | +		EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); + +	if (pixel_clock > MAX_CLOCK) +		pixel_clock = MAX_CLOCK; + +	src_clock = (unsigned long long) get_lcd_clk(); + +	/* get quotient and remainder. */ +	remainder = do_div(src_clock, pixel_clock); +	div = src_clock; + +	remainder *= 10; +	remainder_div = remainder / pixel_clock; + +	/* round about one places of decimals. */ +	if (remainder_div >= 5) +		div++; + +	/* in case of dual lcd mode. */ +	if (pvid->dual_lcd_enabled) +		div--; + +	cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); +	writel(cfg, &fimd_ctrl->vidcon0); +} + +void exynos_set_trigger(void) +{ +	unsigned int cfg = 0; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	cfg = readl(&fimd_ctrl->trigcon); + +	cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); + +	writel(cfg, &fimd_ctrl->trigcon); +} + +int exynos_is_i80_frame_done(void) +{ +	unsigned int cfg = 0; +	int status; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	cfg = readl(&fimd_ctrl->trigcon); + +	/* frame done func is valid only when TRIMODE[0] is set to 1. */ +	status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) == +			EXYNOS_I80STATUS_TRIG_DONE; + +	return status; +} + +static void exynos_fimd_lcd_on(void) +{ +	unsigned int cfg = 0; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	/* display on */ +	cfg = readl(&fimd_ctrl->vidcon0); +	cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); +	writel(cfg, &fimd_ctrl->vidcon0); +} + +static void exynos_fimd_window_on(unsigned int win_id) +{ +	unsigned int cfg = 0; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	/* enable window */ +	cfg = readl((unsigned int)&fimd_ctrl->wincon0 + +			EXYNOS_WINCON(win_id)); +	cfg |= EXYNOS_WINCON_ENWIN_ENABLE; +	writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + +			EXYNOS_WINCON(win_id)); + +	cfg = readl(&fimd_ctrl->winshmap); +	cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); +	writel(cfg, &fimd_ctrl->winshmap); +} + +void exynos_fimd_lcd_off(void) +{ +	unsigned int cfg = 0; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	cfg = readl(&fimd_ctrl->vidcon0); +	cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); +	writel(cfg, &fimd_ctrl->vidcon0); +} + +void exynos_fimd_window_off(unsigned int win_id) +{ +	unsigned int cfg = 0; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	cfg = readl((unsigned int)&fimd_ctrl->wincon0 + +			EXYNOS_WINCON(win_id)); +	cfg &= EXYNOS_WINCON_ENWIN_DISABLE; +	writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + +			EXYNOS_WINCON(win_id)); + +	cfg = readl(&fimd_ctrl->winshmap); +	cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); +	writel(cfg, &fimd_ctrl->winshmap); +} + +void exynos_fimd_lcd_init(vidinfo_t *vid) +{ +	unsigned int cfg = 0, rgb_mode; +	struct exynos4_fb *fimd_ctrl = +		(struct exynos4_fb *)samsung_get_base_fimd(); + +	/* store panel info to global variable */ +	pvid = vid; + +	rgb_mode = MODE_RGB_P; + +	if (vid->interface_mode == FIMD_RGB_INTERFACE) { +		cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; +		writel(cfg, &fimd_ctrl->vidcon0); + +		cfg = readl(&fimd_ctrl->vidcon2); +		cfg &= ~(EXYNOS_VIDCON2_WB_MASK | +			EXYNOS_VIDCON2_TVFORMATSEL_MASK | +			EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); +		cfg |= EXYNOS_VIDCON2_WB_DISABLE; +		writel(cfg, &fimd_ctrl->vidcon2); + +		/* set polarity */ +		cfg = 0; +		if (!pvid->vl_clkp) +			cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; +		if (!pvid->vl_hsp) +			cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; +		if (!pvid->vl_vsp) +			cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; +		if (!pvid->vl_dp) +			cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; + +		writel(cfg, &fimd_ctrl->vidcon1); + +		/* set timing */ +		cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1); +		cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1); +		cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1); +		writel(cfg, &fimd_ctrl->vidtcon0); + +		cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1); +		cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1); +		cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1); + +		writel(cfg, &fimd_ctrl->vidtcon1); + +		/* set lcd size */ +		cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1); +		cfg |= EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1); + +		writel(cfg, &fimd_ctrl->vidtcon2); +	} + +	/* set display mode */ +	cfg = readl(&fimd_ctrl->vidcon0); +	cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; +	cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); +	writel(cfg, &fimd_ctrl->vidcon0); + +	/* set par */ +	exynos_fimd_set_par(pvid->win_id); + +	/* set memory address */ +	exynos_fimd_set_buffer_address(pvid->win_id); + +	/* set buffer size */ +	cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8); +	writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 + +					EXYNOS_BUFFER_SIZE(pvid->win_id)); + +	/* set clock */ +	exynos_fimd_set_clock(pvid); + +	/* set rgb mode to dual lcd. */ +	exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled); + +	/* display on */ +	exynos_fimd_lcd_on(); + +	/* window on */ +	exynos_fimd_window_on(pvid->win_id); +} + +unsigned long exynos_fimd_calc_fbsize(void) +{ +	return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8); +} diff --git a/drivers/video/exynos_mipi_dsi.c b/drivers/video/exynos_mipi_dsi.c new file mode 100644 index 000000000..aee248c84 --- /dev/null +++ b/drivers/video/exynos_mipi_dsi.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <linux/err.h> +#include <asm/arch/dsim.h> +#include <asm/arch/mipi_dsim.h> +#include <asm/arch/power.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clk.h> + +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +#define master_to_driver(a)	(a->dsim_lcd_drv) +#define master_to_device(a)	(a->dsim_lcd_dev) + +static struct exynos_platform_mipi_dsim *dsim_pd; + +struct mipi_dsim_ddi { +	int				bus_id; +	struct list_head		list; +	struct mipi_dsim_lcd_device	*dsim_lcd_dev; +	struct mipi_dsim_lcd_driver	*dsim_lcd_drv; +}; + +static LIST_HEAD(dsim_ddi_list); +static LIST_HEAD(dsim_lcd_dev_list); + +int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) +{ +	struct mipi_dsim_ddi *dsim_ddi; + +	if (!lcd_dev) { +		debug("mipi_dsim_lcd_device is NULL.\n"); +		return -EFAULT; +	} + +	if (!lcd_dev->name) { +		debug("dsim_lcd_device name is NULL.\n"); +		return -EFAULT; +	} + +	dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); +	if (!dsim_ddi) { +		debug("failed to allocate dsim_ddi object.\n"); +		return -EFAULT; +	} + +	dsim_ddi->dsim_lcd_dev = lcd_dev; + +	list_add_tail(&dsim_ddi->list, &dsim_ddi_list); + +	return 0; +} + +struct mipi_dsim_ddi +	*exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv) +{ +	struct mipi_dsim_ddi *dsim_ddi; +	struct mipi_dsim_lcd_device *lcd_dev; + +	list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) { +		lcd_dev = dsim_ddi->dsim_lcd_dev; +		if (!lcd_dev) +			continue; + +		if (lcd_drv->id >= 0) { +			if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0 && +					lcd_drv->id == lcd_dev->id) { +				/** +				 * bus_id would be used to identify +				 * connected bus. +				 */ +				dsim_ddi->bus_id = lcd_dev->bus_id; + +				return dsim_ddi; +			} +		} else { +			if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) { +				/** +				 * bus_id would be used to identify +				 * connected bus. +				 */ +				dsim_ddi->bus_id = lcd_dev->bus_id; + +				return dsim_ddi; +			} +		} + +		kfree(dsim_ddi); +		list_del(&dsim_ddi_list); +	} + +	return NULL; +} + +int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) +{ +	struct mipi_dsim_ddi *dsim_ddi; + +	if (!lcd_drv) { +		debug("mipi_dsim_lcd_driver is NULL.\n"); +		return -EFAULT; +	} + +	if (!lcd_drv->name) { +		debug("dsim_lcd_driver name is NULL.\n"); +		return -EFAULT; +	} + +	dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); +	if (!dsim_ddi) { +		debug("mipi_dsim_ddi object not found.\n"); +		return -EFAULT; +	} + +	dsim_ddi->dsim_lcd_drv = lcd_drv; + +	debug("registered panel driver(%s) to mipi-dsi driver.\n", +		lcd_drv->name); + +	return 0; + +} + +struct mipi_dsim_ddi +	*exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim, +			const char *name) +{ +	struct mipi_dsim_ddi *dsim_ddi; +	struct mipi_dsim_lcd_driver *lcd_drv; +	struct mipi_dsim_lcd_device *lcd_dev; + +	list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) { +		lcd_drv = dsim_ddi->dsim_lcd_drv; +		lcd_dev = dsim_ddi->dsim_lcd_dev; +		if (!lcd_drv || !lcd_dev) +			continue; + +		debug("lcd_drv->id = %d, lcd_dev->id = %d\n", +					lcd_drv->id, lcd_dev->id); + +		if ((strcmp(lcd_drv->name, name) == 0)) { +			lcd_dev->master = dsim; + +			dsim->dsim_lcd_dev = lcd_dev; +			dsim->dsim_lcd_drv = lcd_drv; + +			return dsim_ddi; +		} +	} + +	return NULL; +} + +/* define MIPI-DSI Master operations. */ +static struct mipi_dsim_master_ops master_ops = { +	.cmd_write			= exynos_mipi_dsi_wr_data, +	.get_dsim_frame_done		= exynos_mipi_dsi_get_frame_done_status, +	.clear_dsim_frame_done		= exynos_mipi_dsi_clear_frame_done, +}; + +int exynos_mipi_dsi_init(void) +{ +	struct mipi_dsim_device *dsim; +	struct mipi_dsim_config *dsim_config; +	struct mipi_dsim_ddi *dsim_ddi; + +	dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); +	if (!dsim) { +		debug("failed to allocate dsim object.\n"); +		return -EFAULT; +	} + +	/* get mipi_dsim_config. */ +	dsim_config = dsim_pd->dsim_config; +	if (dsim_config == NULL) { +		debug("failed to get dsim config data.\n"); +		return -EFAULT; +	} + +	dsim->pd = dsim_pd; +	dsim->dsim_config = dsim_config; +	dsim->master_ops = &master_ops; + +	/* bind lcd ddi matched with panel name. */ +	dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); +	if (!dsim_ddi) { +		debug("mipi_dsim_ddi object not found.\n"); +		return -ENOSYS; +	} +	if (dsim_pd->lcd_power) +		dsim_pd->lcd_power(); + +	if (dsim_pd->mipi_power) +		dsim_pd->mipi_power(); + +	/* phy_enable(unsigned int dev_index, unsigned int enable) */ +	if (dsim_pd->phy_enable) +		dsim_pd->phy_enable(0, 1); + +	set_mipi_clk(); + +	exynos_mipi_dsi_init_dsim(dsim); +	exynos_mipi_dsi_init_link(dsim); +	exynos_mipi_dsi_set_hs_enable(dsim); + +	/* set display timing. */ +	exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + +	/* initialize mipi-dsi client(lcd panel). */ +	if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->mipi_panel_init) { +		dsim_ddi->dsim_lcd_drv->mipi_panel_init(dsim); +		dsim_ddi->dsim_lcd_drv->mipi_display_on(dsim); +	} + +	debug("mipi-dsi driver(%s mode) has been probed.\n", +		(dsim_config->e_interface == DSIM_COMMAND) ? +			"CPU" : "RGB"); + +	return 0; +} + +void exynos_set_dsim_platform_data(struct exynos_platform_mipi_dsim *pd) +{ +	if (pd == NULL) { +		debug("pd is NULL\n"); +		return; +	} + +	dsim_pd = pd; +} diff --git a/drivers/video/exynos_mipi_dsi_common.c b/drivers/video/exynos_mipi_dsi_common.c new file mode 100644 index 000000000..6eeb46424 --- /dev/null +++ b/drivers/video/exynos_mipi_dsi_common.c @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <lcd.h> +#include <linux/err.h> +#include <asm/arch/dsim.h> +#include <asm/arch/mipi_dsim.h> + +#include "exynos_mipi_dsi_lowlevel.h" + +#define MHZ			(1000 * 1000) +#define FIN_HZ			(24 * MHZ) + +#define DFIN_PLL_MIN_HZ		(6 * MHZ) +#define DFIN_PLL_MAX_HZ		(12 * MHZ) + +#define DFVCO_MIN_HZ		(500 * MHZ) +#define DFVCO_MAX_HZ		(1000 * MHZ) + +#define TRY_GET_FIFO_TIMEOUT	(5000 * 2) + +/* MIPI-DSIM status types. */ +enum { +	DSIM_STATE_INIT,	/* should be initialized. */ +	DSIM_STATE_STOP,	/* CPU and LCDC are LP mode. */ +	DSIM_STATE_HSCLKEN,	/* HS clock was enabled. */ +	DSIM_STATE_ULPS +}; + +/* define DSI lane types. */ +enum { +	DSIM_LANE_CLOCK = (1 << 0), +	DSIM_LANE_DATA0 = (1 << 1), +	DSIM_LANE_DATA1 = (1 << 2), +	DSIM_LANE_DATA2 = (1 << 3), +	DSIM_LANE_DATA3 = (1 << 4) +}; + +static unsigned int dpll_table[15] = { +	100, 120, 170, 220, 270, +	320, 390, 450, 510, 560, +	640, 690, 770, 870, 950 +}; + +static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim, +		unsigned int data0, unsigned int data1) +{ +	unsigned int data_cnt = 0, payload = 0; + +	/* in case that data count is more then 4 */ +	for (data_cnt = 0; data_cnt < data1; data_cnt += 4) { +		/* +		 * after sending 4bytes per one time, +		 * send remainder data less then 4. +		 */ +		if ((data1 - data_cnt) < 4) { +			if ((data1 - data_cnt) == 3) { +				payload = *(u8 *)(data0 + data_cnt) | +					(*(u8 *)(data0 + (data_cnt + 1))) << 8 | +					(*(u8 *)(data0 + (data_cnt + 2))) << 16; +			debug("count = 3 payload = %x, %x %x %x\n", +				payload, *(u8 *)(data0 + data_cnt), +				*(u8 *)(data0 + (data_cnt + 1)), +				*(u8 *)(data0 + (data_cnt + 2))); +			} else if ((data1 - data_cnt) == 2) { +				payload = *(u8 *)(data0 + data_cnt) | +					(*(u8 *)(data0 + (data_cnt + 1))) << 8; +			debug("count = 2 payload = %x, %x %x\n", payload, +				*(u8 *)(data0 + data_cnt), +				*(u8 *)(data0 + (data_cnt + 1))); +			} else if ((data1 - data_cnt) == 1) { +				payload = *(u8 *)(data0 + data_cnt); +			} +		} else { +			/* send 4bytes per one time. */ +			payload = *(u8 *)(data0 + data_cnt) | +				(*(u8 *)(data0 + (data_cnt + 1))) << 8 | +				(*(u8 *)(data0 + (data_cnt + 2))) << 16 | +				(*(u8 *)(data0 + (data_cnt + 3))) << 24; + +			debug("count = 4 payload = %x, %x %x %x %x\n", +				payload, *(u8 *)(data0 + data_cnt), +				*(u8 *)(data0 + (data_cnt + 1)), +				*(u8 *)(data0 + (data_cnt + 2)), +				*(u8 *)(data0 + (data_cnt + 3))); + +		} +		exynos_mipi_dsi_wr_tx_data(dsim, payload); +	} +} + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, +	unsigned int data0, unsigned int data1) +{ +	unsigned int timeout = TRY_GET_FIFO_TIMEOUT; +	unsigned long delay_val, delay; +	unsigned int check_rx_ack = 0; + +	if (dsim->state == DSIM_STATE_ULPS) { +		debug("state is ULPS.\n"); + +		return -EINVAL; +	} + +	delay_val = MHZ / dsim->dsim_config->esc_clk; +	delay = 10 * delay_val; + +	mdelay(delay); + +	/* only if transfer mode is LPDT, wait SFR becomes empty. */ +	if (dsim->state == DSIM_STATE_STOP) { +		while (!(exynos_mipi_dsi_get_fifo_state(dsim) & +				SFR_HEADER_EMPTY)) { +			if ((timeout--) > 0) +				mdelay(1); +			else { +				debug("SRF header fifo is not empty.\n"); +				return -EINVAL; +			} +		} +	} + +	switch (data_id) { +	/* short packet types of packet types for command. */ +	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: +	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: +	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: +	case MIPI_DSI_DCS_SHORT_WRITE: +	case MIPI_DSI_DCS_SHORT_WRITE_PARAM: +	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: +		debug("data0 = %x data1 = %x\n", +				data0, data1); +		exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0, data1); +		if (check_rx_ack) { +			/* process response func should be implemented */ +			return 0; +		} else { +			return -EINVAL; +		} + +	/* general command */ +	case MIPI_DSI_COLOR_MODE_OFF: +	case MIPI_DSI_COLOR_MODE_ON: +	case MIPI_DSI_SHUTDOWN_PERIPHERAL: +	case MIPI_DSI_TURN_ON_PERIPHERAL: +		exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0, data1); +		if (check_rx_ack) { +			/* process response func should be implemented. */ +			return 0; +		} else { +			return -EINVAL; +		} + +	/* packet types for video data */ +	case MIPI_DSI_V_SYNC_START: +	case MIPI_DSI_V_SYNC_END: +	case MIPI_DSI_H_SYNC_START: +	case MIPI_DSI_H_SYNC_END: +	case MIPI_DSI_END_OF_TRANSMISSION: +		return 0; + +	/* short and response packet types for command */ +	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: +	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: +	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: +	case MIPI_DSI_DCS_READ: +		exynos_mipi_dsi_clear_all_interrupt(dsim); +		exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0, data1); +		/* process response func should be implemented. */ +		return 0; + +	/* long packet type and null packet */ +	case MIPI_DSI_NULL_PACKET: +	case MIPI_DSI_BLANKING_PACKET: +		return 0; +	case MIPI_DSI_GENERIC_LONG_WRITE: +	case MIPI_DSI_DCS_LONG_WRITE: +	{ +		unsigned int data_cnt = 0, payload = 0; + +		/* if data count is less then 4, then send 3bytes data.  */ +		if (data1 < 4) { +			payload = *(u8 *)(data0) | +				*(u8 *)(data0 + 1) << 8 | +				*(u8 *)(data0 + 2) << 16; + +			exynos_mipi_dsi_wr_tx_data(dsim, payload); + +			debug("count = %d payload = %x,%x %x %x\n", +				data1, payload, +				*(u8 *)(data0 + data_cnt), +				*(u8 *)(data0 + (data_cnt + 1)), +				*(u8 *)(data0 + (data_cnt + 2))); +		} else { +			/* in case that data count is more then 4 */ +			exynos_mipi_dsi_long_data_wr(dsim, data0, data1); +		} + +		/* put data into header fifo */ +		exynos_mipi_dsi_wr_tx_header(dsim, data_id, data1 & 0xff, +			(data1 & 0xff00) >> 8); + +	} +	if (check_rx_ack) +		/* process response func should be implemented. */ +		return 0; +	else +		return -EINVAL; + +	/* packet typo for video data */ +	case MIPI_DSI_PACKED_PIXEL_STREAM_16: +	case MIPI_DSI_PACKED_PIXEL_STREAM_18: +	case MIPI_DSI_PIXEL_STREAM_3BYTE_18: +	case MIPI_DSI_PACKED_PIXEL_STREAM_24: +		if (check_rx_ack) { +			/* process response func should be implemented. */ +			return 0; +		} else { +			return -EINVAL; +		} +	default: +		debug("data id %x is not supported current DSI spec.\n", +			data_id); + +		return -EINVAL; +	} + +	return 0; +} + +int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable) +{ +	int sw_timeout; + +	if (enable) { +		sw_timeout = 1000; + +		exynos_mipi_dsi_clear_interrupt(dsim); +		exynos_mipi_dsi_enable_pll(dsim, 1); +		while (1) { +			sw_timeout--; +			if (exynos_mipi_dsi_is_pll_stable(dsim)) +				return 0; +			if (sw_timeout == 0) +				return -EINVAL; +		} +	} else +		exynos_mipi_dsi_enable_pll(dsim, 0); + +	return 0; +} + +unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, +	unsigned int pre_divider, unsigned int main_divider, +	unsigned int scaler) +{ +	unsigned long dfin_pll, dfvco, dpll_out; +	unsigned int i, freq_band = 0xf; + +	dfin_pll = (FIN_HZ / pre_divider); + +	/****************************************************** +	 *	Serial Clock(=ByteClk X 8)	FreqBand[3:0] * +	 ****************************************************** +	 *	~ 99.99 MHz			0000 +	 *	100 ~ 119.99 MHz		0001 +	 *	120 ~ 159.99 MHz		0010 +	 *	160 ~ 199.99 MHz		0011 +	 *	200 ~ 239.99 MHz		0100 +	 *	140 ~ 319.99 MHz		0101 +	 *	320 ~ 389.99 MHz		0110 +	 *	390 ~ 449.99 MHz		0111 +	 *	450 ~ 509.99 MHz		1000 +	 *	510 ~ 559.99 MHz		1001 +	 *	560 ~ 639.99 MHz		1010 +	 *	640 ~ 689.99 MHz		1011 +	 *	690 ~ 769.99 MHz		1100 +	 *	770 ~ 869.99 MHz		1101 +	 *	870 ~ 949.99 MHz		1110 +	 *	950 ~ 1000 MHz			1111 +	 ******************************************************/ +	if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) { +		debug("fin_pll range should be 6MHz ~ 12MHz\n"); +		exynos_mipi_dsi_enable_afc(dsim, 0, 0); +	} else { +		if (dfin_pll < 7 * MHZ) +			exynos_mipi_dsi_enable_afc(dsim, 1, 0x1); +		else if (dfin_pll < 8 * MHZ) +			exynos_mipi_dsi_enable_afc(dsim, 1, 0x0); +		else if (dfin_pll < 9 * MHZ) +			exynos_mipi_dsi_enable_afc(dsim, 1, 0x3); +		else if (dfin_pll < 10 * MHZ) +			exynos_mipi_dsi_enable_afc(dsim, 1, 0x2); +		else if (dfin_pll < 11 * MHZ) +			exynos_mipi_dsi_enable_afc(dsim, 1, 0x5); +		else +			exynos_mipi_dsi_enable_afc(dsim, 1, 0x4); +	} + +	dfvco = dfin_pll * main_divider; +	debug("dfvco = %lu, dfin_pll = %lu, main_divider = %d\n", +				dfvco, dfin_pll, main_divider); +	if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ) +		debug("fvco range should be 500MHz ~ 1000MHz\n"); + +	dpll_out = dfvco / (1 << scaler); +	debug("dpll_out = %lu, dfvco = %lu, scaler = %d\n", +		dpll_out, dfvco, scaler); + +	for (i = 0; i < ARRAY_SIZE(dpll_table); i++) { +		if (dpll_out < dpll_table[i] * MHZ) { +			freq_band = i; +			break; +		} +	} + +	debug("freq_band = %d\n", freq_band); + +	exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler); + +	exynos_mipi_dsi_hs_zero_ctrl(dsim, 0); +	exynos_mipi_dsi_prep_ctrl(dsim, 0); + +	/* Freq Band */ +	exynos_mipi_dsi_pll_freq_band(dsim, freq_band); + +	/* Stable time */ +	exynos_mipi_dsi_pll_stable_time(dsim, +				dsim->dsim_config->pll_stable_time); + +	/* Enable PLL */ +	debug("FOUT of mipi dphy pll is %luMHz\n", +		(dpll_out / MHZ)); + +	return dpll_out; +} + +int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, +	unsigned int byte_clk_sel, unsigned int enable) +{ +	unsigned int esc_div; +	unsigned long esc_clk_error_rate; +	unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0; + +	if (enable) { +		dsim->e_clk_src = byte_clk_sel; + +		/* Escape mode clock and byte clock source */ +		exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel); + +		/* DPHY, DSIM Link : D-PHY clock out */ +		if (byte_clk_sel == DSIM_PLL_OUT_DIV8) { +			hs_clk = exynos_mipi_dsi_change_pll(dsim, +				dsim->dsim_config->p, dsim->dsim_config->m, +				dsim->dsim_config->s); +			if (hs_clk == 0) { +				debug("failed to get hs clock.\n"); +				return -EINVAL; +			} + +			byte_clk = hs_clk / 8; +			exynos_mipi_dsi_enable_pll_bypass(dsim, 0); +			exynos_mipi_dsi_pll_on(dsim, 1); +		/* DPHY : D-PHY clock out, DSIM link : external clock out */ +		} else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) +			debug("not support EXT CLK source for MIPI DSIM\n"); +		else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) +			debug("not support EXT CLK source for MIPI DSIM\n"); + +		/* escape clock divider */ +		esc_div = byte_clk / (dsim->dsim_config->esc_clk); +		debug("esc_div = %d, byte_clk = %lu, esc_clk = %lu\n", +			esc_div, byte_clk, dsim->dsim_config->esc_clk); +		if ((byte_clk / esc_div) >= (20 * MHZ) || +			(byte_clk / esc_div) > dsim->dsim_config->esc_clk) +			esc_div += 1; + +		escape_clk = byte_clk / esc_div; +		debug("escape_clk = %lu, byte_clk = %lu, esc_div = %d\n", +			escape_clk, byte_clk, esc_div); + +		/* enable escape clock. */ +		exynos_mipi_dsi_enable_byte_clock(dsim, 1); + +		/* enable byte clk and escape clock */ +		exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div); +		/* escape clock on lane */ +		exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, +			(DSIM_LANE_CLOCK | dsim->data_lane), 1); + +		debug("byte clock is %luMHz\n", +			(byte_clk / MHZ)); +		debug("escape clock that user's need is %lu\n", +			(dsim->dsim_config->esc_clk / MHZ)); +		debug("escape clock divider is %x\n", esc_div); +		debug("escape clock is %luMHz\n", +			((byte_clk / esc_div) / MHZ)); + +		if ((byte_clk / esc_div) > escape_clk) { +			esc_clk_error_rate = escape_clk / +				(byte_clk / esc_div); +			debug("error rate is %lu over.\n", +				(esc_clk_error_rate / 100)); +		} else if ((byte_clk / esc_div) < (escape_clk)) { +			esc_clk_error_rate = (byte_clk / esc_div) / +				escape_clk; +			debug("error rate is %lu under.\n", +				(esc_clk_error_rate / 100)); +		} +	} else { +		exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, +			(DSIM_LANE_CLOCK | dsim->data_lane), 0); +		exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0); + +		/* disable escape clock. */ +		exynos_mipi_dsi_enable_byte_clock(dsim, 0); + +		if (byte_clk_sel == DSIM_PLL_OUT_DIV8) +			exynos_mipi_dsi_pll_on(dsim, 0); +	} + +	return 0; +} + +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim) +{ +	dsim->state = DSIM_STATE_INIT; + +	switch (dsim->dsim_config->e_no_data_lane) { +	case DSIM_DATA_LANE_1: +		dsim->data_lane = DSIM_LANE_DATA0; +		break; +	case DSIM_DATA_LANE_2: +		dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1; +		break; +	case DSIM_DATA_LANE_3: +		dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | +			DSIM_LANE_DATA2; +		break; +	case DSIM_DATA_LANE_4: +		dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | +			DSIM_LANE_DATA2 | DSIM_LANE_DATA3; +		break; +	default: +		debug("data lane is invalid.\n"); +		return -EINVAL; +	}; + +	exynos_mipi_dsi_sw_reset(dsim); +	exynos_mipi_dsi_dp_dn_swap(dsim, 0); + +	return 0; +} + +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, +	unsigned int enable) +{ +	/* enable only frame done interrupt */ +	exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable); + +	return 0; +} + +static void convert_to_fb_videomode(struct fb_videomode *mode1, +				vidinfo_t *mode2) +{ +	mode1->xres = mode2->vl_width; +	mode1->yres = mode2->vl_height; +	mode1->upper_margin = mode2->vl_vfpd; +	mode1->lower_margin = mode2->vl_vbpd; +	mode1->left_margin = mode2->vl_hfpd; +	mode1->right_margin = mode2->vl_hbpd; +	mode1->vsync_len = mode2->vl_vspw; +	mode1->hsync_len = mode2->vl_hspw; +} + +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, +	struct mipi_dsim_config *dsim_config) +{ +	struct exynos_platform_mipi_dsim *dsim_pd; +	struct fb_videomode lcd_video; +	vidinfo_t *vid; + +	dsim_pd = (struct exynos_platform_mipi_dsim *)dsim->pd; +	vid = (vidinfo_t *)dsim_pd->lcd_panel_info; + +	convert_to_fb_videomode(&lcd_video, vid); + +	/* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ +	if (dsim->dsim_config->e_interface == (u32) DSIM_VIDEO) { +		if (dsim->dsim_config->auto_vertical_cnt == 0) { +			exynos_mipi_dsi_set_main_disp_vporch(dsim, +				vid->vl_cmd_allow_len, +				lcd_video.upper_margin, +				lcd_video.lower_margin); +			exynos_mipi_dsi_set_main_disp_hporch(dsim, +				lcd_video.left_margin, +				lcd_video.right_margin); +			exynos_mipi_dsi_set_main_disp_sync_area(dsim, +				lcd_video.vsync_len, +				lcd_video.hsync_len); +		} +	} + +	exynos_mipi_dsi_set_main_disp_resol(dsim, lcd_video.xres, +			lcd_video.yres); + +	exynos_mipi_dsi_display_config(dsim, dsim->dsim_config); + +	debug("lcd panel ==> width = %d, height = %d\n", +			lcd_video.xres, lcd_video.yres); + +	return 0; +} + +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim) +{ +	unsigned int time_out = 100; + +	switch (dsim->state) { +	case DSIM_STATE_INIT: +		exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f); + +		/* dsi configuration */ +		exynos_mipi_dsi_init_config(dsim); +		exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1); +		exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1); + +		/* set clock configuration */ +		exynos_mipi_dsi_set_clock(dsim, +					dsim->dsim_config->e_byte_clk, 1); + +		/* check clock and data lane state are stop state */ +		while (!(exynos_mipi_dsi_is_lane_state(dsim))) { +			time_out--; +			if (time_out == 0) { +				debug("DSI Master is not stop state.\n"); +				debug("Check initialization process\n"); + +				return -EINVAL; +			} +		} + +		dsim->state = DSIM_STATE_STOP; + +		/* BTA sequence counters */ +		exynos_mipi_dsi_set_stop_state_counter(dsim, +			dsim->dsim_config->stop_holding_cnt); +		exynos_mipi_dsi_set_bta_timeout(dsim, +			dsim->dsim_config->bta_timeout); +		exynos_mipi_dsi_set_lpdr_timeout(dsim, +			dsim->dsim_config->rx_timeout); + +		return 0; +	default: +		debug("DSI Master is already init.\n"); +		return 0; +	} + +	return 0; +} + +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim) +{ +	if (dsim->state == DSIM_STATE_STOP) { +		if (dsim->e_clk_src != DSIM_EXT_CLK_BYPASS) { +			dsim->state = DSIM_STATE_HSCLKEN; + +			 /* set LCDC and CPU transfer mode to HS. */ +			exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); +			exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + +			exynos_mipi_dsi_enable_hs_clock(dsim, 1); + +			return 0; +		} else +			debug("clock source is external bypass.\n"); +	} else +		debug("DSIM is not stop state.\n"); + +	return 0; +} + +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, +		unsigned int mode) +{ +	if (mode) { +		if (dsim->state != DSIM_STATE_HSCLKEN) { +			debug("HS Clock lane is not enabled.\n"); +			return -EINVAL; +		} + +		exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); +	} else { +		if (dsim->state == DSIM_STATE_INIT || dsim->state == +			DSIM_STATE_ULPS) { +			debug("DSI Master is not STOP or HSDT state.\n"); +			return -EINVAL; +		} + +		exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); +	} + +	return 0; +} + +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) +{ +	return _exynos_mipi_dsi_get_frame_done_status(dsim); +} + +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ +	_exynos_mipi_dsi_clear_frame_done(dsim); + +	return 0; +} diff --git a/drivers/video/exynos_mipi_dsi_common.h b/drivers/video/exynos_mipi_dsi_common.h new file mode 100644 index 000000000..4d80679a5 --- /dev/null +++ b/drivers/video/exynos_mipi_dsi_common.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <linux/fb.h> + +#ifndef _EXYNOS_MIPI_DSI_COMMON_H +#define _EXYNOS_MIPI_DSI_COMMON_H + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, +	unsigned int data0, unsigned int data1); +int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable); +unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, +	unsigned int pre_divider, unsigned int main_divider, +	unsigned int scaler); +int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, +	unsigned int byte_clk_sel, unsigned int enable); +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, +			struct mipi_dsim_config *dsim_info); +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, +		unsigned int mode); +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, +	unsigned int enable); +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); + +#endif /* _EXYNOS_MIPI_DSI_COMMON_H */ diff --git a/drivers/video/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos_mipi_dsi_lowlevel.c new file mode 100644 index 000000000..d61b77361 --- /dev/null +++ b/drivers/video/exynos_mipi_dsi_lowlevel.c @@ -0,0 +1,652 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/arch/dsim.h> +#include <asm/arch/mipi_dsim.h> +#include <asm/arch/power.h> +#include <asm/arch/cpu.h> + +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) +{ +	unsigned int reg; + +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = readl(&mipi_dsim->swrst); + +	reg |= DSIM_FUNCRST; + +	writel(reg, &mipi_dsim->swrst); +} + +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim) +{ +	unsigned int reg = 0; + +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = readl(&mipi_dsim->swrst); + +	reg |= DSIM_SWRST; +	reg |= DSIM_FUNCRST; + +	writel(reg, &mipi_dsim->swrst); +} + +void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->intsrc); + +	reg |= INTSRC_SWRST_RELEASE; + +	writel(reg, &mipi_dsim->intsrc); +} + +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, +		unsigned int mode, unsigned int mask) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->intmsk); + +	if (mask) +		reg |= mode; +	else +		reg &= ~mode; + +	writel(reg, &mipi_dsim->intmsk); +} + +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, +		unsigned int cfg) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = readl(&mipi_dsim->fifoctrl); + +	writel(reg & ~(cfg), &mipi_dsim->fifoctrl); +	udelay(10 * 1000); +	reg |= cfg; + +	writel(reg, &mipi_dsim->fifoctrl); +} + +/* + * this function set PLL P, M and S value in D-PHY + */ +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, +		unsigned int value) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	writel(DSIM_AFC_CTL(value), &mipi_dsim->phyacchr); +} + +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, +	unsigned int width_resol, unsigned int height_resol) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	/* standby should be set after configuration so set to not ready*/ +	reg = (readl(&mipi_dsim->mdresol)) & ~(DSIM_MAIN_STAND_BY); +	writel(reg, &mipi_dsim->mdresol); + +	/* reset resolution */ +	reg &= ~(DSIM_MAIN_VRESOL(0x7ff) | DSIM_MAIN_HRESOL(0x7ff)); +	reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol); + +	reg |= DSIM_MAIN_STAND_BY; +	writel(reg, &mipi_dsim->mdresol); +} + +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, +	unsigned int cmd_allow, unsigned int vfront, unsigned int vback) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = (readl(&mipi_dsim->mvporch)) & +		~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) | +		(DSIM_MAIN_VBP_MASK)); + +	reg |= ((cmd_allow & 0xf) << DSIM_CMD_ALLOW_SHIFT) | +		((vfront & 0x7ff) << DSIM_STABLE_VFP_SHIFT) | +		((vback & 0x7ff) << DSIM_MAIN_VBP_SHIFT); + +	writel(reg, &mipi_dsim->mvporch); +} + +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, +	unsigned int front, unsigned int back) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = (readl(&mipi_dsim->mhporch)) & +		~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK)); + +	reg |= (front << DSIM_MAIN_HFP_SHIFT) | (back << DSIM_MAIN_HBP_SHIFT); + +	writel(reg, &mipi_dsim->mhporch); +} + +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, +	unsigned int vert, unsigned int hori) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = (readl(&mipi_dsim->msync)) & +		~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK)); + +	reg |= ((vert & 0x3ff) << DSIM_MAIN_VSA_SHIFT) | +		(hori << DSIM_MAIN_HSA_SHIFT); + +	writel(reg, &mipi_dsim->msync); +} + +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, +	unsigned int vert, unsigned int hori) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = (readl(&mipi_dsim->sdresol)) & +		~(DSIM_SUB_STANDY_MASK); + +	writel(reg, &mipi_dsim->sdresol); + +	reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK); +	reg |= ((vert & 0x7ff) << DSIM_SUB_VRESOL_SHIFT) | +		((hori & 0x7ff) << DSIM_SUB_HRESOL_SHIFT); +	writel(reg, &mipi_dsim->sdresol); + +	/* DSIM STANDBY */ +	reg |= (1 << DSIM_SUB_STANDY_SHIFT); +	writel(reg, &mipi_dsim->sdresol); +} + +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim) +{ +	struct mipi_dsim_config *dsim_config = dsim->dsim_config; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int cfg = (readl(&mipi_dsim->config)) & +		~((1 << DSIM_EOT_PACKET_SHIFT) | +		(0x1f << DSIM_HSA_MODE_SHIFT) | +		(0x3 << DSIM_NUM_OF_DATALANE_SHIFT)); + +	cfg |=	(dsim_config->auto_flush << DSIM_AUTO_FLUSH_SHIFT) | +		(dsim_config->eot_disable << DSIM_EOT_PACKET_SHIFT) | +		(dsim_config->auto_vertical_cnt << DSIM_AUTO_MODE_SHIFT) | +		(dsim_config->hse << DSIM_HSE_MODE_SHIFT) | +		(dsim_config->hfp << DSIM_HFP_MODE_SHIFT) | +		(dsim_config->hbp << DSIM_HBP_MODE_SHIFT) | +		(dsim_config->hsa << DSIM_HSA_MODE_SHIFT) | +		(dsim_config->e_no_data_lane << DSIM_NUM_OF_DATALANE_SHIFT); + +	writel(cfg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, +				struct mipi_dsim_config *dsim_config) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	u32 reg = (readl(&mipi_dsim->config)) & +		~((0x3 << DSIM_BURST_MODE_SHIFT) | (1 << DSIM_VIDEO_MODE_SHIFT) +		| (0x3 << DSIM_MAINVC_SHIFT) | (0x7 << DSIM_MAINPIX_SHIFT) +		| (0x3 << DSIM_SUBVC_SHIFT) | (0x7 << DSIM_SUBPIX_SHIFT)); + +	if (dsim_config->e_interface == DSIM_VIDEO) +		reg |= (1 << DSIM_VIDEO_MODE_SHIFT); +	else if (dsim_config->e_interface == DSIM_COMMAND) +		reg &= ~(1 << DSIM_VIDEO_MODE_SHIFT); +	else { +		printf("unknown lcd type.\n"); +		return; +	} + +	/* main lcd */ +	reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << DSIM_BURST_MODE_SHIFT +	| ((u8) (dsim_config->e_virtual_ch) & 0x3) << DSIM_MAINVC_SHIFT +	| ((u8) (dsim_config->e_pixel_format) & 0x7) << DSIM_MAINPIX_SHIFT; + +	writel(reg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, +			unsigned int lane, unsigned int enable) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = readl(&mipi_dsim->config); + +	if (enable) +		reg |= DSIM_LANE_ENx(lane); +	else +		reg &= ~DSIM_LANE_ENx(lane); + +	writel(reg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, +	unsigned int count) +{ +	unsigned int cfg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	/* get the data lane number. */ +	cfg = DSIM_NUM_OF_DATA_LANE(count); + +	writel(cfg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, +			unsigned int enable, unsigned int afc_code) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->phyacchr); + +	reg = 0; + +	if (enable) { +		reg |= DSIM_AFC_EN; +		reg &= ~(0x7 << DSIM_AFC_CTL_SHIFT); +		reg |= DSIM_AFC_CTL(afc_code); +	} else +		reg &= ~DSIM_AFC_EN; + +	writel(reg, &mipi_dsim->phyacchr); +} + +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, +	unsigned int enable) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->clkctrl)) & +		~(DSIM_PLL_BYPASS_EXTERNAL); + +	reg |= enable << DSIM_PLL_BYPASS_SHIFT; + +	writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, +		unsigned int freq_band) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->pllctrl)) & +		~(0x1f << DSIM_FREQ_BAND_SHIFT); + +	reg |= ((freq_band & 0x1f) << DSIM_FREQ_BAND_SHIFT); + +	writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, +		unsigned int pre_divider, unsigned int main_divider, +		unsigned int scaler) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->pllctrl)) & +		~(0x7ffff << 1); + +	reg |= ((pre_divider & 0x3f) << DSIM_PREDIV_SHIFT) | +		((main_divider & 0x1ff) << DSIM_MAIN_SHIFT) | +		((scaler & 0x7) << DSIM_SCALER_SHIFT); + +	writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, +	unsigned int lock_time) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	writel(lock_time, &mipi_dsim->plltmr); +} + +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, +				unsigned int enable) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->pllctrl)) & +		~(0x1 << DSIM_PLL_EN_SHIFT); + +	reg |= ((enable & 0x1) << DSIM_PLL_EN_SHIFT); + +	writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, +		unsigned int src) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->clkctrl)) & +		~(0x3 << DSIM_BYTE_CLK_SRC_SHIFT); + +	reg |= ((unsigned int) src) << DSIM_BYTE_CLK_SRC_SHIFT; + +	writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, +		unsigned int enable) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->clkctrl)) & +		~(1 << DSIM_BYTE_CLKEN_SHIFT); + +	reg |= enable << DSIM_BYTE_CLKEN_SHIFT; + +	writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, +		unsigned int enable, unsigned int prs_val) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->clkctrl)) & +		~((1 << DSIM_ESC_CLKEN_SHIFT) | (0xffff)); + +	reg |= enable << DSIM_ESC_CLKEN_SHIFT; +	if (enable) +		reg |= prs_val; + +	writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, +		unsigned int lane_sel, unsigned int enable) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->clkctrl); + +	if (enable) +		reg |= DSIM_LANE_ESC_CLKEN(lane_sel); +	else +		reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel); + +	writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, +	unsigned int enable) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->escmode)) & +		~(0x1 << DSIM_FORCE_STOP_STATE_SHIFT); + +	reg |= ((enable & 0x1) << DSIM_FORCE_STOP_STATE_SHIFT); + +	writel(reg, &mipi_dsim->escmode); +} + +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->status); + +	/** +	 * check clock and data lane states. +	 * if MIPI-DSI controller was enabled at bootloader then +	 * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK. +	 * so it should be checked for two case. +	 */ +	if ((reg & DSIM_STOP_STATE_DAT(0xf)) && +			((reg & DSIM_STOP_STATE_CLK) || +			 (reg & DSIM_TX_READY_HS_CLK))) +		return 1; +	else +		return 0; +} + +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, +		unsigned int cnt_val) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->escmode)) & +		~(0x7ff << DSIM_STOP_STATE_CNT_SHIFT); + +	reg |= ((cnt_val & 0x7ff) << DSIM_STOP_STATE_CNT_SHIFT); + +	writel(reg, &mipi_dsim->escmode); +} + +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, +		unsigned int timeout) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->timeout)) & +		~(0xff << DSIM_BTA_TOUT_SHIFT); + +	reg |= (timeout << DSIM_BTA_TOUT_SHIFT); + +	writel(reg, &mipi_dsim->timeout); +} + +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, +		unsigned int timeout) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->timeout)) & +		~(0xffff << DSIM_LPDR_TOUT_SHIFT); + +	reg |= (timeout << DSIM_LPDR_TOUT_SHIFT); + +	writel(reg, &mipi_dsim->timeout); +} + +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, +		unsigned int lp) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->escmode); + +	reg &= ~DSIM_CMD_LPDT_LP; + +	if (lp) +		reg |= DSIM_CMD_LPDT_LP; + +	writel(reg, &mipi_dsim->escmode); +} + +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, +		unsigned int lp) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->escmode); + +	reg &= ~DSIM_TX_LPDT_LP; + +	if (lp) +		reg |= DSIM_TX_LPDT_LP; + +	writel(reg, &mipi_dsim->escmode); +} + +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, +		unsigned int enable) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->clkctrl)) & +		~(1 << DSIM_TX_REQUEST_HSCLK_SHIFT); + +	reg |= enable << DSIM_TX_REQUEST_HSCLK_SHIFT; + +	writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, +		unsigned int swap_en) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->phyacchr1); + +	reg &= ~(0x3 << DSIM_DPDN_SWAP_DATA_SHIFT); +	reg |= (swap_en & 0x3) << DSIM_DPDN_SWAP_DATA_SHIFT; + +	writel(reg, &mipi_dsim->phyacchr1); +} + +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, +		unsigned int hs_zero) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->pllctrl)) & +		~(0xf << DSIM_ZEROCTRL_SHIFT); + +	reg |= ((hs_zero & 0xf) << DSIM_ZEROCTRL_SHIFT); + +	writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (readl(&mipi_dsim->pllctrl)) & +		~(0x7 << DSIM_PRECTRL_SHIFT); + +	reg |= ((prep & 0x7) << DSIM_PRECTRL_SHIFT); + +	writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->intsrc); + +	reg |= INTSRC_PLL_STABLE; + +	writel(reg, &mipi_dsim->intsrc); +} + +void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	writel(0xffffffff, &mipi_dsim->intsrc); +} + +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim) +{ +	unsigned int reg; +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	reg = readl(&mipi_dsim->status); + +	return reg & DSIM_PLL_STABLE ? 1 : 0; +} + +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	return readl(&mipi_dsim->fifoctrl) & ~(0x1f); +} + +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, +	unsigned int di, unsigned int data0, unsigned int data1) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = (DSIM_PKTHDR_DAT1(data1) | DSIM_PKTHDR_DAT0(data0) | +			DSIM_PKTHDR_DI(di)); + +	writel(reg, &mipi_dsim->pkthdr); +} + +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device +						*dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->intsrc); + +	return (reg & INTSRC_FRAME_DONE) ? 1 : 0; +} + +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); +	unsigned int reg = readl(&mipi_dsim->intsrc); + +	writel(reg | INTSRC_FRAME_DONE, &mipi_dsim->intsrc); +} + +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, +		unsigned int tx_data) +{ +	struct exynos_mipi_dsim *mipi_dsim = +		(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + +	writel(tx_data, &mipi_dsim->payload); +} diff --git a/drivers/video/exynos_mipi_dsi_lowlevel.h b/drivers/video/exynos_mipi_dsi_lowlevel.h new file mode 100644 index 000000000..4b8c441cb --- /dev/null +++ b/drivers/video/exynos_mipi_dsi_lowlevel.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H +#define _EXYNOS_MIPI_DSI_LOWLEVEL_H + +void exynos_mipi_dsi_register(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, +	unsigned int mode, unsigned int mask); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, +					unsigned int count); +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, +					unsigned int cfg); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, +				unsigned int value); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, +				unsigned int value); +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, +		unsigned int width_resol, unsigned int height_resol); +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, +	unsigned int cmd_allow, unsigned int vfront, unsigned int vback); +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, +			unsigned int front, unsigned int back); +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, +				unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, +				unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, +				struct mipi_dsim_config *dsim_config); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, +				unsigned int count); +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, +			unsigned int lane, unsigned int enable); +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, +			unsigned int enable, unsigned int afc_code); +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, +				unsigned int enable); +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, +				unsigned int freq_band); +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, +			unsigned int pre_divider, unsigned int main_divider, +			unsigned int scaler); +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, +			unsigned int lock_time); +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, +					unsigned int enable); +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, +					unsigned int src); +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, +					unsigned int enable); +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, +				unsigned int enable, unsigned int prs_val); +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, +				unsigned int lane_sel, unsigned int enable); +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, +				unsigned int enable); +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, +				unsigned int cnt_val); +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, +				unsigned int timeout); +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, +				unsigned int timeout); +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, +					unsigned int lp); +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, +					unsigned int lp); +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, +				unsigned int enable); +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, +				unsigned int swap_en); +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, +				unsigned int hs_zero); +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, +				unsigned int prep); +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim); +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device +						*dsim); +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, +		unsigned int di, unsigned int data0, unsigned int data1); +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, +		unsigned int tx_data); + +#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */ diff --git a/drivers/video/s6e8ax0.c b/drivers/video/s6e8ax0.c new file mode 100644 index 000000000..1ec7fd6e5 --- /dev/null +++ b/drivers/video/s6e8ax0.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/arch/mipi_dsim.h> + +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +static void s6e8ax0_panel_cond(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x4c, +		0x6e, 0x10, 0x27, 0x7d, 0x3f, 0x10, 0x00, 0x00, 0x20, +		0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, +		0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc3, +		0xff, 0xff, 0xc8 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_cond(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xf2, 0x80, 0x03, 0x0d +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_gamma_cond(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	/* 7500K 2.2 Set (M3, 300cd) */ +	const unsigned char data_to_send[] = { +		0xfa, 0x01, 0x0f, 0x00, 0x0f, 0xda, 0xc0, 0xe4, 0xc8, +		0xc8, 0xc6, 0xd3, 0xd6, 0xd0, 0xab, 0xb2, 0xa6, 0xbf, +		0xc2, 0xb9, 0x00, 0x93, 0x00, 0x86, 0x00, 0xd1 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_gamma_update(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xf7, 0x3); +} + +static void s6e8ax0_etc_source_control(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xf6, 0x00, 0x02, 0x00 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_pentile_control(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, +		0x00 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control1(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control2(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_power_control(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +		(unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control3(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xe3, 0x40); +} + +static void s6e8ax0_etc_mipi_control4(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +		(unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_elvss_set(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xb1, 0x04, 0x00 +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +			(unsigned int)data_to_send, +			ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_on(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + +	ops->cmd_write(dsim_dev, +		MIPI_DSI_DCS_SHORT_WRITE, 0x29, 0x00); +} + +static void s6e8ax0_sleep_out(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + +	ops->cmd_write(dsim_dev, +		MIPI_DSI_DCS_SHORT_WRITE, 0x11, 0x00); +} + +static void s6e8ax0_apply_level1_key(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xf0, 0x5a, 0x5a +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +		(unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_apply_mtp_key(struct mipi_dsim_device *dsim_dev) +{ +	struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; +	const unsigned char data_to_send[] = { +		0xf1, 0x5a, 0x5a +	}; + +	ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, +		(unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_panel_init(struct mipi_dsim_device *dsim_dev) +{ +	/* +	 * in case of setting gamma and panel condition at first, +	 * it shuold be setting like below. +	 * set_gamma() -> set_panel_condition() +	 */ + +	s6e8ax0_apply_level1_key(dsim_dev); +	s6e8ax0_apply_mtp_key(dsim_dev); + +	s6e8ax0_sleep_out(dsim_dev); +	mdelay(5); +	s6e8ax0_panel_cond(dsim_dev); +	s6e8ax0_display_cond(dsim_dev); +	s6e8ax0_gamma_cond(dsim_dev); +	s6e8ax0_gamma_update(dsim_dev); + +	s6e8ax0_etc_source_control(dsim_dev); +	s6e8ax0_elvss_set(dsim_dev); +	s6e8ax0_etc_pentile_control(dsim_dev); +	s6e8ax0_etc_mipi_control1(dsim_dev); +	s6e8ax0_etc_mipi_control2(dsim_dev); +	s6e8ax0_etc_power_control(dsim_dev); +	s6e8ax0_etc_mipi_control3(dsim_dev); +	s6e8ax0_etc_mipi_control4(dsim_dev); +} + +static int s6e8ax0_panel_set(struct mipi_dsim_device *dsim_dev) +{ +	s6e8ax0_panel_init(dsim_dev); + +	return 0; +} + +static void s6e8ax0_display_enable(struct mipi_dsim_device *dsim_dev) +{ +	s6e8ax0_display_on(dsim_dev); +} + +static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { +	.name = "s6e8ax0", +	.id = -1, + +	.mipi_panel_init = s6e8ax0_panel_set, +	.mipi_display_on = s6e8ax0_display_enable, +}; + +void s6e8ax0_init(void) +{ +	exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); +}  |