| /* |
| * Copyright (c) 2013 Luis R. Rodriguez <mcgrof@do-not-panic.com> |
| * |
| * Backport compatibility file for Linux for kernels 3.6. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/scatterlist.h> |
| #include <linux/export.h> |
| #include <linux/bug.h> |
| #include <linux/bitmap.h> |
| #include <linux/i2c.h> |
| #include <linux/clk.h> |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) |
| /** |
| * __i2c_transfer - unlocked flavor of i2c_transfer |
| * @adap: Handle to I2C bus |
| * @msgs: One or more messages to execute before STOP is issued to |
| * terminate the operation; each message begins with a START. |
| * @num: Number of messages to be executed. |
| * |
| * Returns negative errno, else the number of messages executed. |
| * |
| * Adapter lock must be held when calling this function. No debug logging |
| * takes place. adap->algo->master_xfer existence isn't checked. |
| */ |
| int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) |
| { |
| unsigned long orig_jiffies; |
| int ret, try; |
| |
| /* Retry automatically on arbitration loss */ |
| orig_jiffies = jiffies; |
| for (ret = 0, try = 0; try <= adap->retries; try++) { |
| ret = adap->algo->master_xfer(adap, msgs, num); |
| if (ret != -EAGAIN) |
| break; |
| if (time_after(jiffies, orig_jiffies + adap->timeout)) |
| break; |
| } |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(__i2c_transfer); |
| #endif |
| |
| /** |
| * memweight - count the total number of bits set in memory area |
| * @ptr: pointer to the start of the area |
| * @bytes: the size of the area |
| */ |
| size_t memweight(const void *ptr, size_t bytes) |
| { |
| size_t ret = 0; |
| size_t longs; |
| const unsigned char *bitmap = ptr; |
| |
| for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long); |
| bytes--, bitmap++) |
| ret += hweight8(*bitmap); |
| |
| longs = bytes / sizeof(long); |
| if (longs) { |
| BUG_ON(longs >= INT_MAX / BITS_PER_LONG); |
| ret += bitmap_weight((unsigned long *)bitmap, |
| longs * BITS_PER_LONG); |
| bytes -= longs * sizeof(long); |
| bitmap += longs * sizeof(long); |
| } |
| /* |
| * The reason that this last loop is distinct from the preceding |
| * bitmap_weight() call is to compute 1-bits in the last region smaller |
| * than sizeof(long) properly on big-endian systems. |
| */ |
| for (; bytes > 0; bytes--, bitmap++) |
| ret += hweight8(*bitmap); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(memweight); |
| |
| /** |
| * sg_alloc_table_from_pages - Allocate and initialize an sg table from |
| * an array of pages |
| * @sgt: The sg table header to use |
| * @pages: Pointer to an array of page pointers |
| * @n_pages: Number of pages in the pages array |
| * @offset: Offset from start of the first page to the start of a buffer |
| * @size: Number of valid bytes in the buffer (after offset) |
| * @gfp_mask: GFP allocation mask |
| * |
| * Description: |
| * Allocate and initialize an sg table from a list of pages. Contiguous |
| * ranges of the pages are squashed into a single scatterlist node. A user |
| * may provide an offset at a start and a size of valid data in a buffer |
| * specified by the page array. The returned sg table is released by |
| * sg_free_table. |
| * |
| * Returns: |
| * 0 on success, negative error on failure |
| */ |
| int sg_alloc_table_from_pages(struct sg_table *sgt, |
| struct page **pages, unsigned int n_pages, |
| unsigned long offset, unsigned long size, |
| gfp_t gfp_mask) |
| { |
| unsigned int chunks; |
| unsigned int i; |
| unsigned int cur_page; |
| int ret; |
| struct scatterlist *s; |
| |
| /* compute number of contiguous chunks */ |
| chunks = 1; |
| for (i = 1; i < n_pages; ++i) |
| if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) |
| ++chunks; |
| |
| ret = sg_alloc_table(sgt, chunks, gfp_mask); |
| if (unlikely(ret)) |
| return ret; |
| |
| /* merging chunks and putting them into the scatterlist */ |
| cur_page = 0; |
| for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { |
| unsigned long chunk_size; |
| unsigned int j; |
| |
| /* look for the end of the current chunk */ |
| for (j = cur_page + 1; j < n_pages; ++j) |
| if (page_to_pfn(pages[j]) != |
| page_to_pfn(pages[j - 1]) + 1) |
| break; |
| |
| chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; |
| sg_set_page(s, pages[cur_page], min(size, chunk_size), offset); |
| size -= chunk_size; |
| offset = 0; |
| cur_page = j; |
| } |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(sg_alloc_table_from_pages); |
| |
| /* whoopsie ! */ |
| #if 0 |
| #ifndef CONFIG_COMMON_CLK |
| int clk_enable(struct clk *clk) |
| { |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(clk_enable); |
| |
| void clk_disable(struct clk *clk) |
| { |
| } |
| EXPORT_SYMBOL_GPL(clk_disable); |
| #endif |
| #endif |