lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20080607122227.GA1500@fluff.org.uk>
Date:	Sat, 7 Jun 2008 13:22:27 +0100
From:	Ben Dooks <ben@...ff.org>
To:	Andrew Morton <akpm@...ux-foundation.org>
Cc:	linux-kernel@...r.kernel.org
Subject: Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel
	<tk@...ntech.de>

On Fri, Jun 06, 2008 at 10:49:28PM -0700, Andrew Morton wrote:
> On Fri, 06 Jun 2008 16:51:18 +0100 Ben Dooks <ben-linux@...ff.org> wrote:
> 
> > This is the latest S3C MMC/SD driver by Thomas Kleffel
> 
> If Thomas was the author then this patch should have had
>
> From: Thomas Kleffel <tk@...ntech.de>

I thought I'd done this for all the relevant patches, will fix thus.
 
> right at the top of the changelog so that the commit is correctly
> attributed.
> 
> > Signed-off-by: Ben Dooks <ben-linux@...ff.org>
> 
> Please pass these patches through chechpatch?  This one alone generates
> 
> total: 40 errors, 29 warnings, 1488 lines checked
> 
> many of which you would have fixed had you been aware of them.  That's
> what checkpatch is for.

Is it ok to fit another patch after this one to fixup these problems?

> >
> > ...
> >
> > +#define dbg(host, channels, args...)		 \
> > +	if (dbgmap_err & channels) 		 \
> > +		dev_err(&host->pdev->dev, args); \
> > +	else if (dbgmap_info & channels)	 \
> > +		dev_info(&host->pdev->dev, args);\
> > +	else if (dbgmap_debug & channels)	 \
> > +		dev_dbg(&host->pdev->dev, args);
> > +
> > +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
> 
> Could be written in C.  That would fix the bug wherein this macro
> evaluates its argument twice.  "resource" is misspelled.
> 
> > +static struct s3c2410_dma_client s3cmci_dma_client = {
> > +	.name		= "s3c-mci",
> > +};
> > +
> > +static void finalize_request(struct s3cmci_host *host);
> > +static void s3cmci_send_request(struct mmc_host *mmc);
> > +static void s3cmci_reset(struct s3cmci_host *host);
> > +
> > +#ifdef CONFIG_MMC_DEBUG
> > +
> > +static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
> 
> too large to inline.

yeah, fix that.

> > +{
> > +	u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
> > +	u32 datcon, datcnt, datsta, fsta, imask;
> > +
> > +	con 	= readl(host->base + S3C2410_SDICON);
> > +	pre 	= readl(host->base + S3C2410_SDIPRE);
> > +	cmdarg 	= readl(host->base + S3C2410_SDICMDARG);
> > +	cmdcon 	= readl(host->base + S3C2410_SDICMDCON);
> > +	cmdsta 	= readl(host->base + S3C2410_SDICMDSTAT);
> > +	r0 	= readl(host->base + S3C2410_SDIRSP0);
> > +	r1 	= readl(host->base + S3C2410_SDIRSP1);
> > +	r2 	= readl(host->base + S3C2410_SDIRSP2);
> > +	r3 	= readl(host->base + S3C2410_SDIRSP3);
> > +	timer 	= readl(host->base + S3C2410_SDITIMER);
> > +	bsize 	= readl(host->base + S3C2410_SDIBSIZE);
> > +	datcon 	= readl(host->base + S3C2410_SDIDCON);
> > +	datcnt 	= readl(host->base + S3C2410_SDIDCNT);
> > +	datsta 	= readl(host->base + S3C2410_SDIDSTA);
> > +	fsta 	= readl(host->base + S3C2410_SDIFSTA);
> > +	imask   = readl(host->base + host->sdiimsk);
> > +
> > +	dbg(host, dbg_debug, "%s  CON:[%08x]  PRE:[%08x]  TMR:[%08x]\n",
> > +				prefix, con, pre, timer);
> > +
> > +	dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
> > +				prefix, cmdcon, cmdarg, cmdsta);
> > +
> > +	dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
> > +			       " DSTA:[%08x] DCNT:[%08x]\n",
> > +				prefix, datcon, fsta, datsta, datcnt);
> > +
> > +	dbg(host, dbg_debug, "%s   R0:[%08x]   R1:[%08x]"
> > +			       "   R2:[%08x]   R3:[%08x]\n",
> > +				prefix, r0, r1, r2, r3);
> > +}
> > +
> >
> > ...
> >
> > +static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
> > +								int fail)
> > +{
> > +	unsigned int dbglvl = fail?dbg_fail:dbg_debug;
> >
> 
> Spaces around the ? and :, please.

fixed.
 
> checkpatch misses this.
> 
> > +	if (!cmd)
> > +		return;
> > +
> > +	if (cmd->error == MMC_ERR_NONE) {
> > +		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
> > +			host->dbgmsg_cmd, cmd->resp[0]);
> > +	} else {
> > +		dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
> > +			cmd->error, host->dbgmsg_cmd, host->status);
> > +	}
> > +
> > +	if (!cmd->data)
> > +		return;
> > +
> > +	if (cmd->data->error == MMC_ERR_NONE) {
> > +		dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
> > +	} else {
> > +		dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
> > +			cmd->data->error, host->dbgmsg_dat,
> > +			readl(host->base + S3C2410_SDIDCNT));
> > +	}
> > +}
> > +#endif
> > +
> > +static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
> > +{
> > +	u32 newmask;
> > +
> > +	newmask = readl(host->base + host->sdiimsk);
> > +	newmask|= imask;
> > +
> > +	writel(newmask, host->base + host->sdiimsk);
> > +
> > +	return newmask;
> > +}
> > +
> > +static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
> > +{
> > +	u32 newmask;
> > +
> > +	newmask = readl(host->base + host->sdiimsk);
> > +	newmask&= ~imask;
> > +
> > +	writel(newmask, host->base + host->sdiimsk);
> > +
> > +	return newmask;
> > +}
> 
> Probably too large to inline.  A useful metric is to build the .o file
> with and without the `inline' statements and see which results in the
> smaller /bin/size output.  Smaller is usuall faster.  And it's smaller.

I'll have a look, but with the function pre-ambles and call overheads
I expect these'll stay inlines.

> > +static inline void clear_imask(struct s3cmci_host *host)
> > +{
> > +	writel(0, host->base + host->sdiimsk);
> > +}
> > +
> > +static inline int get_data_buffer(struct s3cmci_host *host,
> > +			volatile u32 *words, volatile u32 **pointer)
> > +{
> > +	struct scatterlist *sg;
> > +
> > +	if (host->pio_active == XFER_NONE)
> > +		return -EINVAL;
> > +
> > +	if ((!host->mrq) || (!host->mrq->data))
> > +		return -EINVAL;
> > +
> > +	if (host->pio_sgptr >= host->mrq->data->sg_len) {
> > +		dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
> > +		      host->pio_sgptr, host->mrq->data->sg_len);
> > +		return -EBUSY;
> > +	}
> > +	sg = &host->mrq->data->sg[host->pio_sgptr];
> > +
> > +	*words	= sg->length >> 2;
> > +	*pointer= page_address(sg->page) + sg->offset;
> > +
> > +	host->pio_sgptr++;
> > +
> > +	dbg(host, dbg_sg, "new buffer (%i/%i)\n",
> > +	      host->pio_sgptr, host->mrq->data->sg_len);
> > +
> > +	return 0;
> > +}
> 
> This one has three callsites!

Hmm, I'll leave it up to the compiler whether it wants to
try and inline this.

> > +#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
> > +#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
> 
> These didn't need to be implemented as macros.

these are now inlined.

> > +
> > +static inline void do_pio_read(struct s3cmci_host *host)
> > +{
> > +	int res;
> > +	u32 fifo;
> > +	void __iomem *from_ptr;
> > +
> > +	//write real prescaler to host, it might be set slow to fix
> > +	writel(host->prescaler, host->base + S3C2410_SDIPRE);
> > +
> > +	from_ptr = host->base + host->sdidata;
> > +
> > +	while ((fifo = FIFO_FILL(host))) {
> > +		if (!host->pio_words) {
> > +			res = get_data_buffer(host, &host->pio_words,
> > +							&host->pio_ptr);
> > +			if (res) {
> > +				host->pio_active = XFER_NONE;
> > +				host->complete_what = COMPLETION_FINALIZE;
> > +
> > +				dbg(host, dbg_pio, "pio_read(): "
> > +					"complete (no more data).\n");
> > +				return;
> > +			}
> > +
> > +			dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
> > +			       host->pio_words, host->pio_ptr);
> > +		}
> > +
> > +		dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
> > +				   "buffer:[%03i] dcnt:[%08X]\n",
> > +				   fifo, host->pio_words,
> > +				   readl(host->base + S3C2410_SDIDCNT));
> > +
> > +		if (fifo > host->pio_words)
> > +			fifo = host->pio_words;
> > +
> > +		host->pio_words-= fifo;
> > +		host->pio_count+= fifo;
> > +
> > +		while(fifo--) {
> > +			*(host->pio_ptr++) = readl(from_ptr);
> > +		}
> > +	}
> > +
> > +	if (!host->pio_words) {
> > +		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
> > +		if (res) {
> > +			dbg(host, dbg_pio, "pio_read(): "
> > +				"complete (no more buffers).\n");
> > +			host->pio_active = XFER_NONE;
> > +			host->complete_what = COMPLETION_FINALIZE;
> > +
> > +			return;
> > +		}
> > +	}
> > +
> > +	enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
> > +}
> > +
> > +static inline void do_pio_write(struct s3cmci_host *host)
> > +{
> > +	int res;
> > +	u32 fifo;
> > +
> > +	void __iomem *to_ptr;
> > +
> > +	to_ptr = host->base + host->sdidata;
> > +
> > +	while ((fifo = FIFO_FREE(host))) {
> > +		if (!host->pio_words) {
> > +			res = get_data_buffer(host, &host->pio_words,
> > +							&host->pio_ptr);
> > +			if (res) {
> > +				dbg(host, dbg_pio, "pio_write(): "
> > +					"complete (no more data).\n");
> > +				host->pio_active = XFER_NONE;
> > +
> > +				return;
> > +			}
> > +
> > +			dbg(host, dbg_pio, "pio_write(): "
> > +				"new source: [%i]@[%p]\n",
> > +				host->pio_words, host->pio_ptr);
> > +
> > +		}
> > +
> > +		if (fifo > host->pio_words)
> > +			fifo = host->pio_words;
> > +
> > +		host->pio_words-= fifo;
> > +		host->pio_count+= fifo;
> > +
> > +		while(fifo--) {
> > +			writel(*(host->pio_ptr++), to_ptr);
> > +		}
> > +	}
> > +
> > +	enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
> > +}
> 
> Two callsites :(  This is one bloaty driver.

un-inlined.
 
> > +static void pio_tasklet(unsigned long data)
> > +{
> > +	struct s3cmci_host *host = (struct s3cmci_host *) data;
> > +
> > +
> > +	if (host->pio_active == XFER_WRITE)
> > +		do_pio_write(host);
> > +
> > +	if (host->pio_active == XFER_READ)
> > +		do_pio_read(host);
> > +
> > +	if (host->complete_what == COMPLETION_FINALIZE) {
> > +		clear_imask(host);
> > +		if (host->pio_active != XFER_NONE) {
> > +			dbg(host, dbg_err, "unfinished %s "
> > +				"- pio_count:[%u] pio_words:[%u]\n",
> > +				(host->pio_active == XFER_READ)?"read":"write",
> > +				host->pio_count, host->pio_words);
> > +
> > +			host->mrq->data->error = MMC_ERR_DMA;
> > +		}
> > +
> > +		disable_irq(host->irq);
> > +		finalize_request(host);
> > +	}
> > +}
> 
> I see no tasklet_disable() in the driver.  Is there anything to prevent
> the tasklet from running after device close, rmmod, etc?

fixed.
 
> The disable_irq()/enable_irq() on every interrupt will be quite
> inefficient and hostile to any device which shares the interrupt.  WHy
> is it necessary?

do you mean "on every request", no the driver won't be sharing
interrupts with another block, and I would expect that it is to
stop any interrupts when there is no request present, although
I have not checked for this.

> > +/*
> > + * ISR for SDI Interface IRQ
> > + * Communication between driver and ISR works as follows:
> > + *   host->mrq 			points to current request
> > + *   host->complete_what	tells the ISR when the request is considered done
> > + *     COMPLETION_CMDSENT	  when the command was sent
> > + *     COMPLETION_RSPFIN          when a response was received
> > + *     COMPLETION_XFERFINISH	  when the data transfer is finished
> > + *     COMPLETION_XFERFINISH_RSPFIN both of the above.
> > + *   host->complete_request	is the completion-object the driver waits for
> > + *
> > + * 1) Driver sets up host->mrq and host->complete_what
> > + * 2) Driver prepares the transfer
> > + * 3) Driver enables interrupts
> > + * 4) Driver starts transfer
> > + * 5) Driver waits for host->complete_rquest
> > + * 6) ISR checks for request status (errors and success)
> > + * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
> > + * 7) ISR completes host->complete_request
> > + * 8) ISR disables interrupts
> > + * 9) Driver wakes up and takes care of the request
> > + *
> > + * Note: "->error"-fields are expected to be set to 0 before the request
> > + *       was issued by mmc.c - therefore they are only set, when an error
> > + *       contition comes up
> > + */
> > +
> > +static irqreturn_t s3cmci_irq(int irq, void *dev_id)
> > +{
> > +	struct s3cmci_host *host;
> > +	struct mmc_command *cmd;
> > +	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
> > +	u32 mci_cclear, mci_dclear;
> > +	unsigned long iflags;
> > +
> > +	host = (struct s3cmci_host *)dev_id;
> 
> Unneeded and undesirable cast of void*.

fuxed

> > +	spin_lock_irqsave(&host->complete_lock, iflags);
> > +
> > +	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
> > +	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);
> > +	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
> > +	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);
> > +	mci_imsk	= readl(host->base + host->sdiimsk);
> >
> > ...
> >
> > +void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
> > +	int size, enum s3c2410_dma_buffresult result)
> > +{
> > +	unsigned long iflags;
> > +	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
> > +	struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
> > +
> > +	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
> > +	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);
> > +	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);
> > +	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
> > +
> > +	if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
> > +		return;
> > +
> > +	if (!host->dmatogo)
> > +		return;
> 
> Can these things all really happen?

I've not tried finding out.

> > +	spin_lock_irqsave(&host->complete_lock, iflags);
> > +
> > +	if (result != S3C2410_RES_OK) {
> > +		dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
> > +			"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
> > +			mci_csta, mci_dsta, mci_fsta,
> > +			mci_dcnt, result, host->dmatogo);
> > +
> > +		goto fail_request;
> > +	}
> > +
> > +	host->dmatogo--;
> > +	if (host->dmatogo) {
> > +		dbg(host, dbg_dma, "DMA DONE  Size:%i DSTA:[%08x] "
> > +			"DCNT:[%08x] toGo:%u\n",
> > +			size, mci_dsta, mci_dcnt, host->dmatogo);
> > +
> > +		goto out;
> > +	}
> > +
> > +	dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
> > +		size, mci_dsta, mci_dcnt);
> > +
> > +	host->complete_what = COMPLETION_FINALIZE;
> > +
> > +out:
> > +	tasklet_schedule(&host->pio_tasklet);
> > +	spin_unlock_irqrestore(&host->complete_lock, iflags);
> > +	return;
> > +
> > +
> > +fail_request:
> > +	host->mrq->data->error = MMC_ERR_DMA;
> > +	host->complete_what = COMPLETION_FINALIZE;
> > +	writel(0, host->base + host->sdiimsk);
> > +	goto out;
> > +
> > +}
> > +
> >
> > ...
> >
> > +static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
> > +{
> > +	u32 dcon, imsk, stoptries=3;
> > +
> > +	/* write DCON register */
> > +
> > +	if (!data) {
> > +		writel(0, host->base + S3C2410_SDIDCON);
> > +		return 0;
> > +	}
> > +
> > +	while(readl(host->base + S3C2410_SDIDSTA) &
> > +		(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
> > +
> > +		dbg(host, dbg_err,
> > +			"mci_setup_data() transfer stillin progress.\n");
> > +
> > +		writel(0, host->base + S3C2410_SDIDCON);
> > +		s3cmci_reset(host);
> > +
> > +		if (0 == (stoptries--)) {
> 
> Swap 'em, please.  It's not necessary.

fixed

> > +#ifdef CONFIG_MMC_DEBUG
> > +			dbg_dumpregs(host, "DRF");
> > +#endif
> > +
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
> > +
> > +	if (host->dodma) {
> > +		dcon |= S3C2410_SDIDCON_DMAEN;
> > +	}
> > +
> > +	if (host->bus_width == MMC_BUS_WIDTH_4) {
> > +		dcon |= S3C2410_SDIDCON_WIDEBUS;
> > +	}
> > +
> > +	if (!(data->flags & MMC_DATA_STREAM)) {
> > +		dcon |= S3C2410_SDIDCON_BLOCKMODE;
> > +	}
> > +
> > +	if (data->flags & MMC_DATA_WRITE) {
> > +		dcon |= S3C2410_SDIDCON_TXAFTERRESP;
> > +		dcon |= S3C2410_SDIDCON_XFER_TXSTART;
> > +	}
> > +
> > +	if (data->flags & MMC_DATA_READ) {
> > +		dcon |= S3C2410_SDIDCON_RXAFTERCMD;
> > +		dcon |= S3C2410_SDIDCON_XFER_RXSTART;
> > +	}
> > +
> >
> > ...
> >
> > +static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
> > +{
> > +	int dma_len, i;
> > +
> > +	int rw = (data->flags & MMC_DATA_WRITE)?1:0;
> > +
> > +	if (rw != ((data->flags & MMC_DATA_READ)?0:1))
> > +		return -EINVAL;
> 
> That's pretty clumsy looking.
> 
> Setting both MMC_DATA_WRITE and MMC_DATA_READ would be a programming
> bug, wouldn't it?  If so, BUG would be a more appropriate response.

ok, changed.
 
> > +	s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
> > +	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
> > +
> > +	dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
> > +				(rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
> > +
> > +
> > +	if (dma_len == 0)
> > +		return -ENOMEM;
> > +
> > +	host->dma_complete = 0;
> > +	host->dmatogo = dma_len;
> > +
> > +	for (i = 0; i < dma_len; i++) {
> > +		int res;
> > +
> > +		dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
> > +			sg_dma_address(&data->sg[i]),
> > +			sg_dma_len(&data->sg[i]));
> > +
> > +		res = s3c2410_dma_enqueue(host->dma, (void *) host,
> > +				sg_dma_address(&data->sg[i]),
> > +				sg_dma_len(&data->sg[i]));
> > +
> > +		if (res) {
> > +			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
> > +			return -EBUSY;
> > +		}
> > + 	}
> > +
> > +	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
> > +
> > +	return 0;
> > +}
> > +
> >
> > ...
> >
> > +
> > +	host->irq = platform_get_irq(pdev, 0);
> > +	if (host->irq == 0) {
> > +		dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
> > +		ret = -EINVAL;
> > +		goto probe_iounmap;
> > +	}
> > +
> > +	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
> 
> So these interrupts cannot be shared?  The driver will never be used in
> an environment where interrupt sharing is possible?

All current SoCs in the family have one interrupt for each controller,
so this is unlikely.
 
> > +		dev_err(&pdev->dev, "failed to request mci interrupt.\n");
> > +		ret = -ENOENT;
> > +		goto probe_iounmap;
> > +	}
> > +
> > +	disable_irq(host->irq);
> > +
> > +	s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
> > +	set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
> > +
> > +	if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
> > +		dev_err(&pdev->dev,
> > +			"failed to request card detect interrupt.\n");
> > +
> > +		ret = -ENOENT;
> > +		goto probe_free_irq;
> > +	}
> > +
> > +	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
> > +		dev_err(&pdev->dev, "unable to get DMA channel.\n");
> > +		ret = -EBUSY;
> > +		goto probe_free_irq_cd;
> > +	}
> > +
> > +	host->clk = clk_get(&pdev->dev, "sdi");
> > +	if (IS_ERR(host->clk)) {
> > +		dev_err(&pdev->dev, "failed to find clock source.\n");
> > +		ret = PTR_ERR(host->clk);
> > +		host->clk = NULL;
> > +		goto probe_free_host;
> > +	}
> > +
> > +	if ((ret = clk_enable(host->clk))) {
> > +		dev_err(&pdev->dev, "failed to enable clock source.\n");
> > +		goto clk_free;
> > +	}
> > +
> > +	host->clk_rate = clk_get_rate(host->clk);
> > +
> > +	mmc->ops 	= &s3cmci_ops;
> > +	mmc->ocr_avail	= MMC_VDD_32_33;
> > +	mmc->caps	= MMC_CAP_4_BIT_DATA;
> > +	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);
> > +	mmc->f_max 	= host->clk_rate / host->clk_div;
> > +
> > +	mmc->max_blk_count	= 4095;
> > +	mmc->max_blk_size	= 4095;
> > +	mmc->max_req_size	= 4095 * 512;
> > +	mmc->max_seg_size	= mmc->max_req_size;
> > +
> > +	mmc->max_phys_segs	= 128;
> > +	mmc->max_hw_segs	= 128;
> > +
> > +	dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
> > +		(host->is2440?"2440":""),
> > +		host->base, host->irq, host->irq_cd, host->dma);
> > +
> > +	if ((ret = mmc_add_host(mmc))) {
> > +		dev_err(&pdev->dev, "failed to add mmc host.\n");
> > +		goto free_dmabuf;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, mmc);
> > +
> > +	dev_info(&pdev->dev,"initialisation done.\n");
> > +	return 0;
> > +
> > + free_dmabuf:
> > +	clk_disable(host->clk);
> > +
> > + clk_free:
> > +	clk_put(host->clk);
> > +
> > + probe_free_irq_cd:
> > + 	free_irq(host->irq_cd, host);
> > +
> > + probe_free_irq:
> > + 	free_irq(host->irq, host);
> > +
> > + probe_iounmap:
> > +	iounmap(host->base);
> > +
> > + probe_free_mem_region:
> > +	release_mem_region(host->mem->start, RESSIZE(host->mem));
> > +
> > + probe_free_host:
> > +	mmc_free_host(mmc);
> > + probe_out:
> > +	return ret;
> > +}
> > +
> >
> > ...
> >
> > +//FIXME: DMA Resource management ?!
> 
> ?
> 
> > +#define S3CMCI_DMA 0
> > +
> > +enum s3cmci_waitfor {
> > +	COMPLETION_NONE,
> > +	COMPLETION_FINALIZE,
> > +	COMPLETION_CMDSENT,
> > +	COMPLETION_RSPFIN,
> > +	COMPLETION_XFERFINISH,
> > +	COMPLETION_XFERFINISH_RSPFIN,
> > +};
> > +
> > +struct s3cmci_host {
> > +	struct platform_device	*pdev;
> > +	struct mmc_host		*mmc;
> > +	struct resource		*mem;
> > +	struct clk		*clk;
> > +	void __iomem		*base;
> > +	int			irq;
> > +	int			irq_cd;
> > +	int			dma;
> > +
> > +	unsigned long		clk_rate;
> > +	unsigned long		clk_div;
> > +	unsigned long		real_rate;
> > +	u8			prescaler;
> > +
> > +	int			is2440;
> > +	unsigned		sdiimsk;
> > +	unsigned		sdidata;
> > +	int			dodma;
> > +
> > +	volatile int		dmatogo;
> > +
> > +	struct mmc_request	*mrq;
> > +	int			cmd_is_stop;
> > +
> > +	spinlock_t		complete_lock;
> > +	volatile enum s3cmci_waitfor
> > +				complete_what;
> > +
> > +	volatile int		dma_complete;
> > +
> > +	volatile u32		pio_sgptr;
> > +	volatile u32		pio_words;
> > +	volatile u32		pio_count;
> > +	volatile u32		*pio_ptr;
> 
> volatiles.  checkpatch..
> 
> > +#define XFER_NONE 0
> > +#define XFER_READ 1
> > +#define XFER_WRITE 2
> > +	volatile u32		pio_active;
> > +
> > +	int			bus_width;
> > +
> > +	char 			dbgmsg_cmd[301];
> > +	char 			dbgmsg_dat[301];
> > +	volatile char		*status;
> > +
> > +	unsigned int		ccnt, dcnt;
> > +	struct tasklet_struct	pio_tasklet;
> > +};
> 
> Those problems were really really basic stuff.  kernel drivers 101.

Yeah, however I don't want to go about writing a new driver
for this from scratch, and having this interface supported
would add more support for an SoC series used in an number
of PDAs and other devices.

Unfortunatley the original authour isn't interested in getting
it submitted (IE, it works for the kernel he needs to use), and
other people haven't stepped up to try and get this integrated.

-- 
Ben (ben@...ff.org, http://www.fluff.org/)

  'a smiley only costs 4 bytes'
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ