Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into sh-latest

This commit is contained in:
Paul Mundt
2011-03-24 15:17:25 +09:00
429 changed files with 12960 additions and 4413 deletions

View File

@@ -782,6 +782,9 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
if (acpi_video_backlight_support()) {
struct backlight_properties props;
struct pci_dev *pdev;
acpi_handle acpi_parent;
struct device *parent = NULL;
int result;
static int count = 0;
char *name;
@@ -794,9 +797,20 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
return;
count++;
acpi_get_parent(device->dev->handle, &acpi_parent);
pdev = acpi_get_pci_dev(acpi_parent);
if (pdev) {
parent = &pdev->dev;
pci_dev_put(pdev);
}
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_FIRMWARE;
props.max_brightness = device->brightness->count - 3;
device->backlight = backlight_device_register(name, NULL, device,
device->backlight = backlight_device_register(name,
parent,
device,
&acpi_backlight_ops,
&props);
kfree(name);

View File

@@ -31,6 +31,7 @@
#include <linux/ceph/osd_client.h>
#include <linux/ceph/mon_client.h>
#include <linux/ceph/decode.h>
#include <linux/parser.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -54,6 +55,8 @@
#define DEV_NAME_LEN 32
#define RBD_NOTIFY_TIMEOUT_DEFAULT 10
/*
* block device image metadata (in-memory version)
*/
@@ -71,6 +74,12 @@ struct rbd_image_header {
char *snap_names;
u64 *snap_sizes;
u64 obj_version;
};
struct rbd_options {
int notify_timeout;
};
/*
@@ -78,6 +87,7 @@ struct rbd_image_header {
*/
struct rbd_client {
struct ceph_client *client;
struct rbd_options *rbd_opts;
struct kref kref;
struct list_head node;
};
@@ -124,6 +134,9 @@ struct rbd_device {
char pool_name[RBD_MAX_POOL_NAME_LEN];
int poolid;
struct ceph_osd_event *watch_event;
struct ceph_osd_request *watch_request;
char snap_name[RBD_MAX_SNAP_NAME_LEN];
u32 cur_snap; /* index+1 of current snapshot within snap context
0 - for the head */
@@ -177,6 +190,8 @@ static void rbd_put_dev(struct rbd_device *rbd_dev)
put_device(&rbd_dev->dev);
}
static int __rbd_update_snaps(struct rbd_device *rbd_dev);
static int rbd_open(struct block_device *bdev, fmode_t mode)
{
struct gendisk *disk = bdev->bd_disk;
@@ -211,7 +226,8 @@ static const struct block_device_operations rbd_bd_ops = {
* Initialize an rbd client instance.
* We own *opt.
*/
static struct rbd_client *rbd_client_create(struct ceph_options *opt)
static struct rbd_client *rbd_client_create(struct ceph_options *opt,
struct rbd_options *rbd_opts)
{
struct rbd_client *rbdc;
int ret = -ENOMEM;
@@ -233,6 +249,8 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt)
if (ret < 0)
goto out_err;
rbdc->rbd_opts = rbd_opts;
spin_lock(&node_lock);
list_add_tail(&rbdc->node, &rbd_client_list);
spin_unlock(&node_lock);
@@ -266,6 +284,59 @@ static struct rbd_client *__rbd_client_find(struct ceph_options *opt)
return NULL;
}
/*
* mount options
*/
enum {
Opt_notify_timeout,
Opt_last_int,
/* int args above */
Opt_last_string,
/* string args above */
};
static match_table_t rbdopt_tokens = {
{Opt_notify_timeout, "notify_timeout=%d"},
/* int args above */
/* string args above */
{-1, NULL}
};
static int parse_rbd_opts_token(char *c, void *private)
{
struct rbd_options *rbdopt = private;
substring_t argstr[MAX_OPT_ARGS];
int token, intval, ret;
token = match_token((char *)c, rbdopt_tokens, argstr);
if (token < 0)
return -EINVAL;
if (token < Opt_last_int) {
ret = match_int(&argstr[0], &intval);
if (ret < 0) {
pr_err("bad mount option arg (not int) "
"at '%s'\n", c);
return ret;
}
dout("got int token %d val %d\n", token, intval);
} else if (token > Opt_last_int && token < Opt_last_string) {
dout("got string token %d val %s\n", token,
argstr[0].from);
} else {
dout("got token %d\n", token);
}
switch (token) {
case Opt_notify_timeout:
rbdopt->notify_timeout = intval;
break;
default:
BUG_ON(token);
}
return 0;
}
/*
* Get a ceph client with specific addr and configuration, if one does
* not exist create it.
@@ -276,11 +347,18 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr,
struct rbd_client *rbdc;
struct ceph_options *opt;
int ret;
struct rbd_options *rbd_opts;
rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL);
if (!rbd_opts)
return -ENOMEM;
rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT;
ret = ceph_parse_options(&opt, options, mon_addr,
mon_addr + strlen(mon_addr), NULL, NULL);
mon_addr + strlen(mon_addr), parse_rbd_opts_token, rbd_opts);
if (ret < 0)
return ret;
goto done_err;
spin_lock(&node_lock);
rbdc = __rbd_client_find(opt);
@@ -296,13 +374,18 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr,
}
spin_unlock(&node_lock);
rbdc = rbd_client_create(opt);
if (IS_ERR(rbdc))
return PTR_ERR(rbdc);
rbdc = rbd_client_create(opt, rbd_opts);
if (IS_ERR(rbdc)) {
ret = PTR_ERR(rbdc);
goto done_err;
}
rbd_dev->rbd_client = rbdc;
rbd_dev->client = rbdc->client;
return 0;
done_err:
kfree(rbd_opts);
return ret;
}
/*
@@ -318,6 +401,7 @@ static void rbd_client_release(struct kref *kref)
spin_unlock(&node_lock);
ceph_destroy_client(rbdc->client);
kfree(rbdc->rbd_opts);
kfree(rbdc);
}
@@ -666,7 +750,9 @@ static int rbd_do_request(struct request *rq,
struct ceph_osd_req_op *ops,
int num_reply,
void (*rbd_cb)(struct ceph_osd_request *req,
struct ceph_msg *msg))
struct ceph_msg *msg),
struct ceph_osd_request **linger_req,
u64 *ver)
{
struct ceph_osd_request *req;
struct ceph_file_layout *layout;
@@ -729,12 +815,20 @@ static int rbd_do_request(struct request *rq,
req->r_oid, req->r_oid_len);
up_read(&header->snap_rwsem);
if (linger_req) {
ceph_osdc_set_request_linger(&dev->client->osdc, req);
*linger_req = req;
}
ret = ceph_osdc_start_request(&dev->client->osdc, req, false);
if (ret < 0)
goto done_err;
if (!rbd_cb) {
ret = ceph_osdc_wait_request(&dev->client->osdc, req);
if (ver)
*ver = le64_to_cpu(req->r_reassert_version.version);
dout("reassert_ver=%lld\n", le64_to_cpu(req->r_reassert_version.version));
ceph_osdc_put_request(req);
}
return ret;
@@ -789,6 +883,11 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
kfree(req_data);
}
static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
{
ceph_osdc_put_request(req);
}
/*
* Do a synchronous ceph osd operation
*/
@@ -801,7 +900,9 @@ static int rbd_req_sync_op(struct rbd_device *dev,
int num_reply,
const char *obj,
u64 ofs, u64 len,
char *buf)
char *buf,
struct ceph_osd_request **linger_req,
u64 *ver)
{
int ret;
struct page **pages;
@@ -833,7 +934,8 @@ static int rbd_req_sync_op(struct rbd_device *dev,
flags,
ops,
2,
NULL);
NULL,
linger_req, ver);
if (ret < 0)
goto done_ops;
@@ -893,7 +995,7 @@ static int rbd_do_op(struct request *rq,
flags,
ops,
num_reply,
rbd_req_cb);
rbd_req_cb, 0, NULL);
done:
kfree(seg_name);
return ret;
@@ -940,18 +1042,174 @@ static int rbd_req_sync_read(struct rbd_device *dev,
u64 snapid,
const char *obj,
u64 ofs, u64 len,
char *buf)
char *buf,
u64 *ver)
{
return rbd_req_sync_op(dev, NULL,
(snapid ? snapid : CEPH_NOSNAP),
CEPH_OSD_OP_READ,
CEPH_OSD_FLAG_READ,
NULL,
1, obj, ofs, len, buf);
1, obj, ofs, len, buf, NULL, ver);
}
/*
* Request sync osd read
* Request sync osd watch
*/
static int rbd_req_sync_notify_ack(struct rbd_device *dev,
u64 ver,
u64 notify_id,
const char *obj)
{
struct ceph_osd_req_op *ops;
struct page **pages = NULL;
int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0);
if (ret < 0)
return ret;
ops[0].watch.ver = cpu_to_le64(dev->header.obj_version);
ops[0].watch.cookie = notify_id;
ops[0].watch.flag = 0;
ret = rbd_do_request(NULL, dev, NULL, CEPH_NOSNAP,
obj, 0, 0, NULL,
pages, 0,
CEPH_OSD_FLAG_READ,
ops,
1,
rbd_simple_req_cb, 0, NULL);
rbd_destroy_ops(ops);
return ret;
}
static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
{
struct rbd_device *dev = (struct rbd_device *)data;
if (!dev)
return;
dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
notify_id, (int)opcode);
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
__rbd_update_snaps(dev);
mutex_unlock(&ctl_mutex);
rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name);
}
/*
* Request sync osd watch
*/
static int rbd_req_sync_watch(struct rbd_device *dev,
const char *obj,
u64 ver)
{
struct ceph_osd_req_op *ops;
struct ceph_osd_client *osdc = &dev->client->osdc;
int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
if (ret < 0)
return ret;
ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0,
(void *)dev, &dev->watch_event);
if (ret < 0)
goto fail;
ops[0].watch.ver = cpu_to_le64(ver);
ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
ops[0].watch.flag = 1;
ret = rbd_req_sync_op(dev, NULL,
CEPH_NOSNAP,
0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
1, obj, 0, 0, NULL,
&dev->watch_request, NULL);
if (ret < 0)
goto fail_event;
rbd_destroy_ops(ops);
return 0;
fail_event:
ceph_osdc_cancel_event(dev->watch_event);
dev->watch_event = NULL;
fail:
rbd_destroy_ops(ops);
return ret;
}
struct rbd_notify_info {
struct rbd_device *dev;
};
static void rbd_notify_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
{
struct rbd_device *dev = (struct rbd_device *)data;
if (!dev)
return;
dout("rbd_notify_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
notify_id, (int)opcode);
}
/*
* Request sync osd notify
*/
static int rbd_req_sync_notify(struct rbd_device *dev,
const char *obj)
{
struct ceph_osd_req_op *ops;
struct ceph_osd_client *osdc = &dev->client->osdc;
struct ceph_osd_event *event;
struct rbd_notify_info info;
int payload_len = sizeof(u32) + sizeof(u32);
int ret;
ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY, payload_len);
if (ret < 0)
return ret;
info.dev = dev;
ret = ceph_osdc_create_event(osdc, rbd_notify_cb, 1,
(void *)&info, &event);
if (ret < 0)
goto fail;
ops[0].watch.ver = 1;
ops[0].watch.flag = 1;
ops[0].watch.cookie = event->cookie;
ops[0].watch.prot_ver = RADOS_NOTIFY_VER;
ops[0].watch.timeout = 12;
ret = rbd_req_sync_op(dev, NULL,
CEPH_NOSNAP,
0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
1, obj, 0, 0, NULL, NULL, NULL);
if (ret < 0)
goto fail_event;
ret = ceph_osdc_wait_event(event, CEPH_OSD_TIMEOUT_DEFAULT);
dout("ceph_osdc_wait_event returned %d\n", ret);
rbd_destroy_ops(ops);
return 0;
fail_event:
ceph_osdc_cancel_event(event);
fail:
rbd_destroy_ops(ops);
return ret;
}
/*
* Request sync osd rollback
*/
static int rbd_req_sync_rollback_obj(struct rbd_device *dev,
u64 snapid,
@@ -969,13 +1227,10 @@ static int rbd_req_sync_rollback_obj(struct rbd_device *dev,
0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
1, obj, 0, 0, NULL);
1, obj, 0, 0, NULL, NULL, NULL);
rbd_destroy_ops(ops);
if (ret < 0)
return ret;
return ret;
}
@@ -987,7 +1242,8 @@ static int rbd_req_sync_exec(struct rbd_device *dev,
const char *cls,
const char *method,
const char *data,
int len)
int len,
u64 *ver)
{
struct ceph_osd_req_op *ops;
int cls_len = strlen(cls);
@@ -1010,7 +1266,7 @@ static int rbd_req_sync_exec(struct rbd_device *dev,
0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
1, obj, 0, 0, NULL);
1, obj, 0, 0, NULL, NULL, ver);
rbd_destroy_ops(ops);
@@ -1156,6 +1412,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
struct rbd_image_header_ondisk *dh;
int snap_count = 0;
u64 snap_names_len = 0;
u64 ver;
while (1) {
int len = sizeof(*dh) +
@@ -1171,7 +1428,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
NULL, CEPH_NOSNAP,
rbd_dev->obj_md_name,
0, len,
(char *)dh);
(char *)dh, &ver);
if (rc < 0)
goto out_dh;
@@ -1188,6 +1445,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
}
break;
}
header->obj_version = ver;
out_dh:
kfree(dh);
@@ -1205,6 +1463,7 @@ static int rbd_header_add_snap(struct rbd_device *dev,
u64 new_snapid;
int ret;
void *data, *data_start, *data_end;
u64 ver;
/* we should create a snapshot only if we're pointing at the head */
if (dev->cur_snap)
@@ -1227,7 +1486,7 @@ static int rbd_header_add_snap(struct rbd_device *dev,
ceph_encode_64_safe(&data, data_end, new_snapid, bad);
ret = rbd_req_sync_exec(dev, dev->obj_md_name, "rbd", "snap_add",
data_start, data - data_start);
data_start, data - data_start, &ver);
kfree(data_start);
@@ -1259,6 +1518,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
int ret;
struct rbd_image_header h;
u64 snap_seq;
int follow_seq = 0;
ret = rbd_read_header(rbd_dev, &h);
if (ret < 0)
@@ -1267,6 +1527,11 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
down_write(&rbd_dev->header.snap_rwsem);
snap_seq = rbd_dev->header.snapc->seq;
if (rbd_dev->header.total_snaps &&
rbd_dev->header.snapc->snaps[0] == snap_seq)
/* pointing at the head, will need to follow that
if head moves */
follow_seq = 1;
kfree(rbd_dev->header.snapc);
kfree(rbd_dev->header.snap_names);
@@ -1277,7 +1542,10 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
rbd_dev->header.snap_names = h.snap_names;
rbd_dev->header.snap_names_len = h.snap_names_len;
rbd_dev->header.snap_sizes = h.snap_sizes;
rbd_dev->header.snapc->seq = snap_seq;
if (follow_seq)
rbd_dev->header.snapc->seq = rbd_dev->header.snapc->snaps[0];
else
rbd_dev->header.snapc->seq = snap_seq;
ret = __rbd_init_snaps_header(rbd_dev);
@@ -1699,7 +1967,28 @@ static void rbd_bus_del_dev(struct rbd_device *rbd_dev)
device_unregister(&rbd_dev->dev);
}
static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count)
static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
{
int ret, rc;
do {
ret = rbd_req_sync_watch(rbd_dev, rbd_dev->obj_md_name,
rbd_dev->header.obj_version);
if (ret == -ERANGE) {
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
rc = __rbd_update_snaps(rbd_dev);
mutex_unlock(&ctl_mutex);
if (rc < 0)
return rc;
}
} while (ret == -ERANGE);
return ret;
}
static ssize_t rbd_add(struct bus_type *bus,
const char *buf,
size_t count)
{
struct ceph_osd_client *osdc;
struct rbd_device *rbd_dev;
@@ -1797,6 +2086,10 @@ static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count)
if (rc)
goto err_out_bus;
rc = rbd_init_watch_dev(rbd_dev);
if (rc)
goto err_out_bus;
return count;
err_out_bus:
@@ -1849,6 +2142,12 @@ static void rbd_dev_release(struct device *dev)
struct rbd_device *rbd_dev =
container_of(dev, struct rbd_device, dev);
if (rbd_dev->watch_request)
ceph_osdc_unregister_linger_request(&rbd_dev->client->osdc,
rbd_dev->watch_request);
if (rbd_dev->watch_event)
ceph_osdc_cancel_event(rbd_dev->watch_event);
rbd_put_client(rbd_dev);
/* clean up and free blkdev */
@@ -1914,14 +2213,24 @@ static ssize_t rbd_snap_add(struct device *dev,
ret = rbd_header_add_snap(rbd_dev,
name, GFP_KERNEL);
if (ret < 0)
goto done_unlock;
goto err_unlock;
ret = __rbd_update_snaps(rbd_dev);
if (ret < 0)
goto done_unlock;
goto err_unlock;
/* shouldn't hold ctl_mutex when notifying.. notify might
trigger a watch callback that would need to get that mutex */
mutex_unlock(&ctl_mutex);
/* make a best effort, don't error if failed */
rbd_req_sync_notify(rbd_dev, rbd_dev->obj_md_name);
ret = count;
done_unlock:
kfree(name);
return ret;
err_unlock:
mutex_unlock(&ctl_mutex);
kfree(name);
return ret;

View File

@@ -111,10 +111,8 @@ static void unregister_dca_providers(void)
/* at this point only one domain in the list is expected */
domain = list_first_entry(&dca_domains, struct dca_domain, node);
list_for_each_entry_safe(dca, _dca, &domain->dca_providers, node) {
list_del(&dca->node);
list_add(&dca->node, &unregistered_providers);
}
list_for_each_entry_safe(dca, _dca, &domain->dca_providers, node)
list_move(&dca->node, &unregistered_providers);
dca_free_domain(domain);

View File

@@ -82,7 +82,7 @@ config INTEL_IOP_ADMA
config DW_DMAC
tristate "Synopsys DesignWare AHB DMA support"
depends on AVR32
depends on HAVE_CLK
select DMA_ENGINE
default y if CPU_AT32AP7000
help
@@ -221,12 +221,20 @@ config IMX_SDMA
config IMX_DMA
tristate "i.MX DMA support"
depends on ARCH_MX1 || ARCH_MX21 || MACH_MX27
depends on IMX_HAVE_DMA_V1
select DMA_ENGINE
help
Support the i.MX DMA engine. This engine is integrated into
Freescale i.MX1/21/27 chips.
config MXS_DMA
bool "MXS DMA support"
depends on SOC_IMX23 || SOC_IMX28
select DMA_ENGINE
help
Support the MXS DMA engine. This engine including APBH-DMA
and APBX-DMA is integrated into Freescale i.MX23/28 chips.
config DMA_ENGINE
bool

View File

@@ -19,6 +19,7 @@ obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
obj-$(CONFIG_IMX_SDMA) += imx-sdma.o
obj-$(CONFIG_IMX_DMA) += imx-dma.o
obj-$(CONFIG_MXS_DMA) += mxs-dma.o
obj-$(CONFIG_TIMB_DMA) += timb_dma.o
obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
obj-$(CONFIG_PL330_DMA) += pl330.o

View File

@@ -54,6 +54,11 @@ module_param(pq_sources, uint, S_IRUGO);
MODULE_PARM_DESC(pq_sources,
"Number of p+q source buffers (default: 3)");
static int timeout = 3000;
module_param(timeout, uint, S_IRUGO);
MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), \
Pass -1 for infinite timeout");
/*
* Initialization patterns. All bytes in the source buffer has bit 7
* set, all bytes in the destination buffer has bit 7 cleared.
@@ -285,7 +290,12 @@ static int dmatest_func(void *data)
set_user_nice(current, 10);
flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT;
/*
* src buffers are freed by the DMAEngine code with dma_unmap_single()
* dst buffers are freed by ourselves below
*/
flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT
| DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SRC_UNMAP_SINGLE;
while (!kthread_should_stop()
&& !(iterations && total_tests >= iterations)) {
@@ -294,7 +304,7 @@ static int dmatest_func(void *data)
dma_addr_t dma_srcs[src_cnt];
dma_addr_t dma_dsts[dst_cnt];
struct completion cmp;
unsigned long tmo = msecs_to_jiffies(3000);
unsigned long tmo = msecs_to_jiffies(timeout);
u8 align = 0;
total_tests++;

View File

@@ -32,26 +32,30 @@
* which does not support descriptor writeback.
*/
/* NOTE: DMS+SMS is system-specific. We should get this information
* from the platform code somehow.
*/
#define DWC_DEFAULT_CTLLO (DWC_CTLL_DST_MSIZE(0) \
| DWC_CTLL_SRC_MSIZE(0) \
| DWC_CTLL_DMS(0) \
| DWC_CTLL_SMS(1) \
| DWC_CTLL_LLP_D_EN \
| DWC_CTLL_LLP_S_EN)
#define DWC_DEFAULT_CTLLO(private) ({ \
struct dw_dma_slave *__slave = (private); \
int dms = __slave ? __slave->dst_master : 0; \
int sms = __slave ? __slave->src_master : 1; \
u8 smsize = __slave ? __slave->src_msize : DW_DMA_MSIZE_16; \
u8 dmsize = __slave ? __slave->dst_msize : DW_DMA_MSIZE_16; \
\
(DWC_CTLL_DST_MSIZE(dmsize) \
| DWC_CTLL_SRC_MSIZE(smsize) \
| DWC_CTLL_LLP_D_EN \
| DWC_CTLL_LLP_S_EN \
| DWC_CTLL_DMS(dms) \
| DWC_CTLL_SMS(sms)); \
})
/*
* This is configuration-dependent and usually a funny size like 4095.
* Let's round it down to the nearest power of two.
*
* Note that this is a transfer count, i.e. if we transfer 32-bit
* words, we can do 8192 bytes per descriptor.
* words, we can do 16380 bytes per descriptor.
*
* This parameter is also system-specific.
*/
#define DWC_MAX_COUNT 2048U
#define DWC_MAX_COUNT 4095U
/*
* Number of descriptors to allocate for each channel. This should be
@@ -84,11 +88,6 @@ static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc)
return list_entry(dwc->active_list.next, struct dw_desc, desc_node);
}
static struct dw_desc *dwc_first_queued(struct dw_dma_chan *dwc)
{
return list_entry(dwc->queue.next, struct dw_desc, desc_node);
}
static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
{
struct dw_desc *desc, *_desc;
@@ -201,6 +200,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc)
dma_async_tx_callback callback;
void *param;
struct dma_async_tx_descriptor *txd = &desc->txd;
struct dw_desc *child;
dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie);
@@ -209,6 +209,12 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc)
param = txd->callback_param;
dwc_sync_desc_for_cpu(dwc, desc);
/* async_tx_ack */
list_for_each_entry(child, &desc->tx_list, desc_node)
async_tx_ack(&child->txd);
async_tx_ack(&desc->txd);
list_splice_init(&desc->tx_list, &dwc->free_list);
list_move(&desc->desc_node, &dwc->free_list);
@@ -259,10 +265,11 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
* Submit queued descriptors ASAP, i.e. before we go through
* the completed ones.
*/
if (!list_empty(&dwc->queue))
dwc_dostart(dwc, dwc_first_queued(dwc));
list_splice_init(&dwc->active_list, &list);
list_splice_init(&dwc->queue, &dwc->active_list);
if (!list_empty(&dwc->queue)) {
list_move(dwc->queue.next, &dwc->active_list);
dwc_dostart(dwc, dwc_first_active(dwc));
}
list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc);
@@ -291,6 +298,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
return;
}
if (list_empty(&dwc->active_list))
return;
dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%x\n", llp);
list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
@@ -319,8 +329,8 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
cpu_relax();
if (!list_empty(&dwc->queue)) {
dwc_dostart(dwc, dwc_first_queued(dwc));
list_splice_init(&dwc->queue, &dwc->active_list);
list_move(dwc->queue.next, &dwc->active_list);
dwc_dostart(dwc, dwc_first_active(dwc));
}
}
@@ -346,7 +356,7 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
*/
bad_desc = dwc_first_active(dwc);
list_del_init(&bad_desc->desc_node);
list_splice_init(&dwc->queue, dwc->active_list.prev);
list_move(dwc->queue.next, dwc->active_list.prev);
/* Clear the error flag and try to restart the controller */
dma_writel(dw, CLEAR.ERROR, dwc->mask);
@@ -541,8 +551,8 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
if (list_empty(&dwc->active_list)) {
dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n",
desc->txd.cookie);
dwc_dostart(dwc, desc);
list_add_tail(&desc->desc_node, &dwc->active_list);
dwc_dostart(dwc, dwc_first_active(dwc));
} else {
dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u\n",
desc->txd.cookie);
@@ -581,14 +591,16 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
* We can be a lot more clever here, but this should take care
* of the most common optimization.
*/
if (!((src | dest | len) & 3))
if (!((src | dest | len) & 7))
src_width = dst_width = 3;
else if (!((src | dest | len) & 3))
src_width = dst_width = 2;
else if (!((src | dest | len) & 1))
src_width = dst_width = 1;
else
src_width = dst_width = 0;
ctllo = DWC_DEFAULT_CTLLO
ctllo = DWC_DEFAULT_CTLLO(chan->private)
| DWC_CTLL_DST_WIDTH(dst_width)
| DWC_CTLL_SRC_WIDTH(src_width)
| DWC_CTLL_DST_INC
@@ -669,11 +681,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
switch (direction) {
case DMA_TO_DEVICE:
ctllo = (DWC_DEFAULT_CTLLO
ctllo = (DWC_DEFAULT_CTLLO(chan->private)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_FIX
| DWC_CTLL_SRC_INC
| DWC_CTLL_FC_M2P);
| DWC_CTLL_FC(dws->fc));
reg = dws->tx_reg;
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
@@ -714,11 +726,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
}
break;
case DMA_FROM_DEVICE:
ctllo = (DWC_DEFAULT_CTLLO
ctllo = (DWC_DEFAULT_CTLLO(chan->private)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_INC
| DWC_CTLL_SRC_FIX
| DWC_CTLL_FC_P2M);
| DWC_CTLL_FC(dws->fc));
reg = dws->rx_reg;
for_each_sg(sgl, sg, sg_len, i) {
@@ -834,7 +846,9 @@ dwc_tx_status(struct dma_chan *chan,
ret = dma_async_is_complete(cookie, last_complete, last_used);
if (ret != DMA_SUCCESS) {
spin_lock_bh(&dwc->lock);
dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
spin_unlock_bh(&dwc->lock);
last_complete = dwc->completed;
last_used = chan->cookie;
@@ -889,8 +903,11 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev);
cfghi = dws->cfg_hi;
cfglo = dws->cfg_lo;
cfglo = dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK;
}
cfglo |= DWC_CFGL_CH_PRIOR(dwc->priority);
channel_writel(dwc, CFG_LO, cfglo);
channel_writel(dwc, CFG_HI, cfghi);
@@ -1126,23 +1143,23 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
case DMA_TO_DEVICE:
desc->lli.dar = dws->tx_reg;
desc->lli.sar = buf_addr + (period_len * i);
desc->lli.ctllo = (DWC_DEFAULT_CTLLO
desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_FIX
| DWC_CTLL_SRC_INC
| DWC_CTLL_FC_M2P
| DWC_CTLL_FC(dws->fc)
| DWC_CTLL_INT_EN);
break;
case DMA_FROM_DEVICE:
desc->lli.dar = buf_addr + (period_len * i);
desc->lli.sar = dws->rx_reg;
desc->lli.ctllo = (DWC_DEFAULT_CTLLO
desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_INC
| DWC_CTLL_SRC_FIX
| DWC_CTLL_FC_P2M
| DWC_CTLL_FC(dws->fc)
| DWC_CTLL_INT_EN);
break;
default:
@@ -1307,7 +1324,17 @@ static int __init dw_probe(struct platform_device *pdev)
dwc->chan.device = &dw->dma;
dwc->chan.cookie = dwc->completed = 1;
dwc->chan.chan_id = i;
list_add_tail(&dwc->chan.device_node, &dw->dma.channels);
if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING)
list_add_tail(&dwc->chan.device_node,
&dw->dma.channels);
else
list_add(&dwc->chan.device_node, &dw->dma.channels);
/* 7 is highest priority & 0 is lowest. */
if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING)
dwc->priority = 7 - i;
else
dwc->priority = i;
dwc->ch_regs = &__dw_regs(dw)->CHAN[i];
spin_lock_init(&dwc->lock);
@@ -1335,6 +1362,8 @@ static int __init dw_probe(struct platform_device *pdev)
dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask);
dma_cap_set(DMA_SLAVE, dw->dma.cap_mask);
if (pdata->is_private)
dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
dw->dma.dev = &pdev->dev;
dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources;
dw->dma.device_free_chan_resources = dwc_free_chan_resources;
@@ -1447,7 +1476,7 @@ static int __init dw_init(void)
{
return platform_driver_probe(&dw_driver, dw_probe);
}
module_init(dw_init);
subsys_initcall(dw_init);
static void __exit dw_exit(void)
{

View File

@@ -86,6 +86,7 @@ struct dw_dma_regs {
#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14)
#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */
#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */
#define DWC_CTLL_FC(n) ((n) << 20)
#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */
#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */
#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */
@@ -101,6 +102,8 @@ struct dw_dma_regs {
#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff
/* Bitfields in CFG_LO. Platform-configurable bits are in <linux/dw_dmac.h> */
#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */
#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */
#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */
#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */
#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */
@@ -134,6 +137,7 @@ struct dw_dma_chan {
struct dma_chan chan;
void __iomem *ch_regs;
u8 mask;
u8 priority;
spinlock_t lock;
@@ -155,9 +159,9 @@ __dwc_regs(struct dw_dma_chan *dwc)
}
#define channel_readl(dwc, name) \
__raw_readl(&(__dwc_regs(dwc)->name))
readl(&(__dwc_regs(dwc)->name))
#define channel_writel(dwc, name, val) \
__raw_writel((val), &(__dwc_regs(dwc)->name))
writel((val), &(__dwc_regs(dwc)->name))
static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan)
{
@@ -181,9 +185,9 @@ static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw)
}
#define dma_readl(dw, name) \
__raw_readl(&(__dw_regs(dw)->name))
readl(&(__dw_regs(dw)->name))
#define dma_writel(dw, name, val) \
__raw_writel((val), &(__dw_regs(dw)->name))
writel((val), &(__dw_regs(dw)->name))
#define channel_set_bit(dw, reg, mask) \
dma_writel(dw, reg, ((mask) << 8) | (mask))

View File

@@ -37,35 +37,16 @@
#include "fsldma.h"
static const char msg_ld_oom[] = "No free memory for link descriptor\n";
#define chan_dbg(chan, fmt, arg...) \
dev_dbg(chan->dev, "%s: " fmt, chan->name, ##arg)
#define chan_err(chan, fmt, arg...) \
dev_err(chan->dev, "%s: " fmt, chan->name, ##arg)
static void dma_init(struct fsldma_chan *chan)
{
/* Reset the channel */
DMA_OUT(chan, &chan->regs->mr, 0, 32);
static const char msg_ld_oom[] = "No free memory for link descriptor";
switch (chan->feature & FSL_DMA_IP_MASK) {
case FSL_DMA_IP_85XX:
/* Set the channel to below modes:
* EIE - Error interrupt enable
* EOSIE - End of segments interrupt enable (basic mode)
* EOLNIE - End of links interrupt enable
* BWC - Bandwidth sharing among channels
*/
DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC
| FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE
| FSL_DMA_MR_EOSIE, 32);
break;
case FSL_DMA_IP_83XX:
/* Set the channel to below modes:
* EOTIE - End-of-transfer interrupt enable
* PRC_RM - PCI read multiple
*/
DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EOTIE
| FSL_DMA_MR_PRC_RM, 32);
break;
}
}
/*
* Register Helpers
*/
static void set_sr(struct fsldma_chan *chan, u32 val)
{
@@ -77,42 +58,6 @@ static u32 get_sr(struct fsldma_chan *chan)
return DMA_IN(chan, &chan->regs->sr, 32);
}
static void set_desc_cnt(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, u32 count)
{
hw->count = CPU_TO_DMA(chan, count, 32);
}
static void set_desc_src(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, dma_addr_t src)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0;
hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64);
}
static void set_desc_dst(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, dma_addr_t dst)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0;
hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64);
}
static void set_desc_next(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, dma_addr_t next)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
? FSL_DMA_SNEN : 0;
hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64);
}
static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
{
DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
@@ -123,70 +68,77 @@ static dma_addr_t get_cdar(struct fsldma_chan *chan)
return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
}
static dma_addr_t get_ndar(struct fsldma_chan *chan)
{
return DMA_IN(chan, &chan->regs->ndar, 64);
}
static u32 get_bcr(struct fsldma_chan *chan)
{
return DMA_IN(chan, &chan->regs->bcr, 32);
}
static int dma_is_idle(struct fsldma_chan *chan)
/*
* Descriptor Helpers
*/
static void set_desc_cnt(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, u32 count)
{
u32 sr = get_sr(chan);
return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH);
hw->count = CPU_TO_DMA(chan, count, 32);
}
static void dma_start(struct fsldma_chan *chan)
static u32 get_desc_cnt(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
{
u32 mode;
mode = DMA_IN(chan, &chan->regs->mr, 32);
if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
DMA_OUT(chan, &chan->regs->bcr, 0, 32);
mode |= FSL_DMA_MR_EMP_EN;
} else {
mode &= ~FSL_DMA_MR_EMP_EN;
}
}
if (chan->feature & FSL_DMA_CHAN_START_EXT)
mode |= FSL_DMA_MR_EMS_EN;
else
mode |= FSL_DMA_MR_CS;
DMA_OUT(chan, &chan->regs->mr, mode, 32);
return DMA_TO_CPU(chan, desc->hw.count, 32);
}
static void dma_halt(struct fsldma_chan *chan)
static void set_desc_src(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, dma_addr_t src)
{
u32 mode;
int i;
u64 snoop_bits;
mode = DMA_IN(chan, &chan->regs->mr, 32);
mode |= FSL_DMA_MR_CA;
DMA_OUT(chan, &chan->regs->mr, mode, 32);
mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN | FSL_DMA_MR_CA);
DMA_OUT(chan, &chan->regs->mr, mode, 32);
for (i = 0; i < 100; i++) {
if (dma_is_idle(chan))
return;
udelay(10);
}
if (!dma_is_idle(chan))
dev_err(chan->dev, "DMA halt timeout!\n");
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0;
hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64);
}
static void set_ld_eol(struct fsldma_chan *chan,
struct fsl_desc_sw *desc)
static dma_addr_t get_desc_src(struct fsldma_chan *chan,
struct fsl_desc_sw *desc)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0;
return DMA_TO_CPU(chan, desc->hw.src_addr, 64) & ~snoop_bits;
}
static void set_desc_dst(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, dma_addr_t dst)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0;
hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64);
}
static dma_addr_t get_desc_dst(struct fsldma_chan *chan,
struct fsl_desc_sw *desc)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0;
return DMA_TO_CPU(chan, desc->hw.dst_addr, 64) & ~snoop_bits;
}
static void set_desc_next(struct fsldma_chan *chan,
struct fsl_dma_ld_hw *hw, dma_addr_t next)
{
u64 snoop_bits;
snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
? FSL_DMA_SNEN : 0;
hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64);
}
static void set_ld_eol(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
{
u64 snoop_bits;
@@ -198,6 +150,108 @@ static void set_ld_eol(struct fsldma_chan *chan,
| snoop_bits, 64);
}
/*
* DMA Engine Hardware Control Helpers
*/
static void dma_init(struct fsldma_chan *chan)
{
/* Reset the channel */
DMA_OUT(chan, &chan->regs->mr, 0, 32);
switch (chan->feature & FSL_DMA_IP_MASK) {
case FSL_DMA_IP_85XX:
/* Set the channel to below modes:
* EIE - Error interrupt enable
* EOLNIE - End of links interrupt enable
* BWC - Bandwidth sharing among channels
*/
DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC
| FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE, 32);
break;
case FSL_DMA_IP_83XX:
/* Set the channel to below modes:
* EOTIE - End-of-transfer interrupt enable
* PRC_RM - PCI read multiple
*/
DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EOTIE
| FSL_DMA_MR_PRC_RM, 32);
break;
}
}
static int dma_is_idle(struct fsldma_chan *chan)
{
u32 sr = get_sr(chan);
return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH);
}
/*
* Start the DMA controller
*
* Preconditions:
* - the CDAR register must point to the start descriptor
* - the MRn[CS] bit must be cleared
*/
static void dma_start(struct fsldma_chan *chan)
{
u32 mode;
mode = DMA_IN(chan, &chan->regs->mr, 32);
if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
DMA_OUT(chan, &chan->regs->bcr, 0, 32);
mode |= FSL_DMA_MR_EMP_EN;
} else {
mode &= ~FSL_DMA_MR_EMP_EN;
}
if (chan->feature & FSL_DMA_CHAN_START_EXT) {
mode |= FSL_DMA_MR_EMS_EN;
} else {
mode &= ~FSL_DMA_MR_EMS_EN;
mode |= FSL_DMA_MR_CS;
}
DMA_OUT(chan, &chan->regs->mr, mode, 32);
}
static void dma_halt(struct fsldma_chan *chan)
{
u32 mode;
int i;
/* read the mode register */
mode = DMA_IN(chan, &chan->regs->mr, 32);
/*
* The 85xx controller supports channel abort, which will stop
* the current transfer. On 83xx, this bit is the transfer error
* mask bit, which should not be changed.
*/
if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
mode |= FSL_DMA_MR_CA;
DMA_OUT(chan, &chan->regs->mr, mode, 32);
mode &= ~FSL_DMA_MR_CA;
}
/* stop the DMA controller */
mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN);
DMA_OUT(chan, &chan->regs->mr, mode, 32);
/* wait for the DMA controller to become idle */
for (i = 0; i < 100; i++) {
if (dma_is_idle(chan))
return;
udelay(10);
}
if (!dma_is_idle(chan))
chan_err(chan, "DMA halt timeout!\n");
}
/**
* fsl_chan_set_src_loop_size - Set source address hold transfer size
* @chan : Freescale DMA channel
@@ -321,8 +375,7 @@ static void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable)
chan->feature &= ~FSL_DMA_CHAN_START_EXT;
}
static void append_ld_queue(struct fsldma_chan *chan,
struct fsl_desc_sw *desc)
static void append_ld_queue(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
{
struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev);
@@ -363,8 +416,8 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
cookie = chan->common.cookie;
list_for_each_entry(child, &desc->tx_list, node) {
cookie++;
if (cookie < 0)
cookie = 1;
if (cookie < DMA_MIN_COOKIE)
cookie = DMA_MIN_COOKIE;
child->async_tx.cookie = cookie;
}
@@ -385,15 +438,14 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
*
* Return - The descriptor allocated. NULL for failed.
*/
static struct fsl_desc_sw *fsl_dma_alloc_descriptor(
struct fsldma_chan *chan)
static struct fsl_desc_sw *fsl_dma_alloc_descriptor(struct fsldma_chan *chan)
{
struct fsl_desc_sw *desc;
dma_addr_t pdesc;
desc = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &pdesc);
if (!desc) {
dev_dbg(chan->dev, "out of memory for link desc\n");
chan_dbg(chan, "out of memory for link descriptor\n");
return NULL;
}
@@ -403,10 +455,13 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor(
desc->async_tx.tx_submit = fsl_dma_tx_submit;
desc->async_tx.phys = pdesc;
#ifdef FSL_DMA_LD_DEBUG
chan_dbg(chan, "LD %p allocated\n", desc);
#endif
return desc;
}
/**
* fsl_dma_alloc_chan_resources - Allocate resources for DMA channel.
* @chan : Freescale DMA channel
@@ -427,13 +482,11 @@ static int fsl_dma_alloc_chan_resources(struct dma_chan *dchan)
* We need the descriptor to be aligned to 32bytes
* for meeting FSL DMA specification requirement.
*/
chan->desc_pool = dma_pool_create("fsl_dma_engine_desc_pool",
chan->dev,
chan->desc_pool = dma_pool_create(chan->name, chan->dev,
sizeof(struct fsl_desc_sw),
__alignof__(struct fsl_desc_sw), 0);
if (!chan->desc_pool) {
dev_err(chan->dev, "unable to allocate channel %d "
"descriptor pool\n", chan->id);
chan_err(chan, "unable to allocate descriptor pool\n");
return -ENOMEM;
}
@@ -455,6 +508,9 @@ static void fsldma_free_desc_list(struct fsldma_chan *chan,
list_for_each_entry_safe(desc, _desc, list, node) {
list_del(&desc->node);
#ifdef FSL_DMA_LD_DEBUG
chan_dbg(chan, "LD %p free\n", desc);
#endif
dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
}
}
@@ -466,6 +522,9 @@ static void fsldma_free_desc_list_reverse(struct fsldma_chan *chan,
list_for_each_entry_safe_reverse(desc, _desc, list, node) {
list_del(&desc->node);
#ifdef FSL_DMA_LD_DEBUG
chan_dbg(chan, "LD %p free\n", desc);
#endif
dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
}
}
@@ -479,7 +538,7 @@ static void fsl_dma_free_chan_resources(struct dma_chan *dchan)
struct fsldma_chan *chan = to_fsl_chan(dchan);
unsigned long flags;
dev_dbg(chan->dev, "Free all channel resources.\n");
chan_dbg(chan, "free all channel resources\n");
spin_lock_irqsave(&chan->desc_lock, flags);
fsldma_free_desc_list(chan, &chan->ld_pending);
fsldma_free_desc_list(chan, &chan->ld_running);
@@ -502,7 +561,7 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags)
new = fsl_dma_alloc_descriptor(chan);
if (!new) {
dev_err(chan->dev, msg_ld_oom);
chan_err(chan, "%s\n", msg_ld_oom);
return NULL;
}
@@ -512,14 +571,15 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags)
/* Insert the link descriptor to the LD ring */
list_add_tail(&new->node, &new->tx_list);
/* Set End-of-link to the last link descriptor of new list*/
/* Set End-of-link to the last link descriptor of new list */
set_ld_eol(chan, new);
return &new->async_tx;
}
static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
struct dma_chan *dchan, dma_addr_t dma_dst, dma_addr_t dma_src,
static struct dma_async_tx_descriptor *
fsl_dma_prep_memcpy(struct dma_chan *dchan,
dma_addr_t dma_dst, dma_addr_t dma_src,
size_t len, unsigned long flags)
{
struct fsldma_chan *chan;
@@ -539,12 +599,9 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
/* Allocate the link descriptor from DMA pool */
new = fsl_dma_alloc_descriptor(chan);
if (!new) {
dev_err(chan->dev, msg_ld_oom);
chan_err(chan, "%s\n", msg_ld_oom);
goto fail;
}
#ifdef FSL_DMA_LD_DEBUG
dev_dbg(chan->dev, "new link desc alloc %p\n", new);
#endif
copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT);
@@ -572,7 +629,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
new->async_tx.flags = flags; /* client is in control of this ack */
new->async_tx.cookie = -EBUSY;
/* Set End-of-link to the last link descriptor of new list*/
/* Set End-of-link to the last link descriptor of new list */
set_ld_eol(chan, new);
return &first->async_tx;
@@ -627,12 +684,9 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan,
/* allocate and populate the descriptor */
new = fsl_dma_alloc_descriptor(chan);
if (!new) {
dev_err(chan->dev, msg_ld_oom);
chan_err(chan, "%s\n", msg_ld_oom);
goto fail;
}
#ifdef FSL_DMA_LD_DEBUG
dev_dbg(chan->dev, "new link desc alloc %p\n", new);
#endif
set_desc_cnt(chan, &new->hw, len);
set_desc_src(chan, &new->hw, src);
@@ -744,14 +798,15 @@ static int fsl_dma_device_control(struct dma_chan *dchan,
switch (cmd) {
case DMA_TERMINATE_ALL:
spin_lock_irqsave(&chan->desc_lock, flags);
/* Halt the DMA engine */
dma_halt(chan);
spin_lock_irqsave(&chan->desc_lock, flags);
/* Remove and free all of the descriptors in the LD queue */
fsldma_free_desc_list(chan, &chan->ld_pending);
fsldma_free_desc_list(chan, &chan->ld_running);
chan->idle = true;
spin_unlock_irqrestore(&chan->desc_lock, flags);
return 0;
@@ -789,139 +844,86 @@ static int fsl_dma_device_control(struct dma_chan *dchan,
}
/**
* fsl_dma_update_completed_cookie - Update the completed cookie.
* @chan : Freescale DMA channel
*
* CONTEXT: hardirq
*/
static void fsl_dma_update_completed_cookie(struct fsldma_chan *chan)
{
struct fsl_desc_sw *desc;
unsigned long flags;
dma_cookie_t cookie;
spin_lock_irqsave(&chan->desc_lock, flags);
if (list_empty(&chan->ld_running)) {
dev_dbg(chan->dev, "no running descriptors\n");
goto out_unlock;
}
/* Get the last descriptor, update the cookie to that */
desc = to_fsl_desc(chan->ld_running.prev);
if (dma_is_idle(chan))
cookie = desc->async_tx.cookie;
else {
cookie = desc->async_tx.cookie - 1;
if (unlikely(cookie < DMA_MIN_COOKIE))
cookie = DMA_MAX_COOKIE;
}
chan->completed_cookie = cookie;
out_unlock:
spin_unlock_irqrestore(&chan->desc_lock, flags);
}
/**
* fsldma_desc_status - Check the status of a descriptor
* fsldma_cleanup_descriptor - cleanup and free a single link descriptor
* @chan: Freescale DMA channel
* @desc: DMA SW descriptor
* @desc: descriptor to cleanup and free
*
* This function will return the status of the given descriptor
* This function is used on a descriptor which has been executed by the DMA
* controller. It will run any callbacks, submit any dependencies, and then
* free the descriptor.
*/
static enum dma_status fsldma_desc_status(struct fsldma_chan *chan,
struct fsl_desc_sw *desc)
static void fsldma_cleanup_descriptor(struct fsldma_chan *chan,
struct fsl_desc_sw *desc)
{
return dma_async_is_complete(desc->async_tx.cookie,
chan->completed_cookie,
chan->common.cookie);
}
struct dma_async_tx_descriptor *txd = &desc->async_tx;
struct device *dev = chan->common.device->dev;
dma_addr_t src = get_desc_src(chan, desc);
dma_addr_t dst = get_desc_dst(chan, desc);
u32 len = get_desc_cnt(chan, desc);
/**
* fsl_chan_ld_cleanup - Clean up link descriptors
* @chan : Freescale DMA channel
*
* This function clean up the ld_queue of DMA channel.
*/
static void fsl_chan_ld_cleanup(struct fsldma_chan *chan)
{
struct fsl_desc_sw *desc, *_desc;
unsigned long flags;
spin_lock_irqsave(&chan->desc_lock, flags);
dev_dbg(chan->dev, "chan completed_cookie = %d\n", chan->completed_cookie);
list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) {
dma_async_tx_callback callback;
void *callback_param;
if (fsldma_desc_status(chan, desc) == DMA_IN_PROGRESS)
break;
/* Remove from the list of running transactions */
list_del(&desc->node);
/* Run the link descriptor callback function */
callback = desc->async_tx.callback;
callback_param = desc->async_tx.callback_param;
if (callback) {
spin_unlock_irqrestore(&chan->desc_lock, flags);
dev_dbg(chan->dev, "LD %p callback\n", desc);
callback(callback_param);
spin_lock_irqsave(&chan->desc_lock, flags);
}
/* Run any dependencies, then free the descriptor */
dma_run_dependencies(&desc->async_tx);
dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
/* Run the link descriptor callback function */
if (txd->callback) {
#ifdef FSL_DMA_LD_DEBUG
chan_dbg(chan, "LD %p callback\n", desc);
#endif
txd->callback(txd->callback_param);
}
spin_unlock_irqrestore(&chan->desc_lock, flags);
/* Run any dependencies */
dma_run_dependencies(txd);
/* Unmap the dst buffer, if requested */
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
dma_unmap_single(dev, dst, len, DMA_FROM_DEVICE);
else
dma_unmap_page(dev, dst, len, DMA_FROM_DEVICE);
}
/* Unmap the src buffer, if requested */
if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
dma_unmap_single(dev, src, len, DMA_TO_DEVICE);
else
dma_unmap_page(dev, src, len, DMA_TO_DEVICE);
}
#ifdef FSL_DMA_LD_DEBUG
chan_dbg(chan, "LD %p free\n", desc);
#endif
dma_pool_free(chan->desc_pool, desc, txd->phys);
}
/**
* fsl_chan_xfer_ld_queue - transfer any pending transactions
* @chan : Freescale DMA channel
*
* This will make sure that any pending transactions will be run.
* If the DMA controller is idle, it will be started. Otherwise,
* the DMA controller's interrupt handler will start any pending
* transactions when it becomes idle.
* HARDWARE STATE: idle
* LOCKING: must hold chan->desc_lock
*/
static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan)
{
struct fsl_desc_sw *desc;
unsigned long flags;
spin_lock_irqsave(&chan->desc_lock, flags);
/*
* If the list of pending descriptors is empty, then we
* don't need to do any work at all
*/
if (list_empty(&chan->ld_pending)) {
dev_dbg(chan->dev, "no pending LDs\n");
goto out_unlock;
chan_dbg(chan, "no pending LDs\n");
return;
}
/*
* The DMA controller is not idle, which means the interrupt
* handler will start any queued transactions when it runs
* at the end of the current transaction
* The DMA controller is not idle, which means that the interrupt
* handler will start any queued transactions when it runs after
* this transaction finishes
*/
if (!dma_is_idle(chan)) {
dev_dbg(chan->dev, "DMA controller still busy\n");
goto out_unlock;
if (!chan->idle) {
chan_dbg(chan, "DMA controller still busy\n");
return;
}
/*
* TODO:
* make sure the dma_halt() function really un-wedges the
* controller as much as possible
*/
dma_halt(chan);
/*
* If there are some link descriptors which have not been
* transferred, we need to start the controller
@@ -931,18 +933,32 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan)
* Move all elements from the queue of pending transactions
* onto the list of running transactions
*/
chan_dbg(chan, "idle, starting controller\n");
desc = list_first_entry(&chan->ld_pending, struct fsl_desc_sw, node);
list_splice_tail_init(&chan->ld_pending, &chan->ld_running);
/*
* The 85xx DMA controller doesn't clear the channel start bit
* automatically at the end of a transfer. Therefore we must clear
* it in software before starting the transfer.
*/
if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
u32 mode;
mode = DMA_IN(chan, &chan->regs->mr, 32);
mode &= ~FSL_DMA_MR_CS;
DMA_OUT(chan, &chan->regs->mr, mode, 32);
}
/*
* Program the descriptor's address into the DMA controller,
* then start the DMA transaction
*/
set_cdar(chan, desc->async_tx.phys);
dma_start(chan);
get_cdar(chan);
out_unlock:
spin_unlock_irqrestore(&chan->desc_lock, flags);
dma_start(chan);
chan->idle = false;
}
/**
@@ -952,7 +968,11 @@ out_unlock:
static void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan)
{
struct fsldma_chan *chan = to_fsl_chan(dchan);
unsigned long flags;
spin_lock_irqsave(&chan->desc_lock, flags);
fsl_chan_xfer_ld_queue(chan);
spin_unlock_irqrestore(&chan->desc_lock, flags);
}
/**
@@ -964,16 +984,18 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan,
struct dma_tx_state *txstate)
{
struct fsldma_chan *chan = to_fsl_chan(dchan);
dma_cookie_t last_used;
dma_cookie_t last_complete;
dma_cookie_t last_used;
unsigned long flags;
fsl_chan_ld_cleanup(chan);
spin_lock_irqsave(&chan->desc_lock, flags);
last_used = dchan->cookie;
last_complete = chan->completed_cookie;
last_used = dchan->cookie;
spin_unlock_irqrestore(&chan->desc_lock, flags);
dma_set_tx_state(txstate, last_complete, last_used, 0);
return dma_async_is_complete(cookie, last_complete, last_used);
}
@@ -984,21 +1006,20 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan,
static irqreturn_t fsldma_chan_irq(int irq, void *data)
{
struct fsldma_chan *chan = data;
int update_cookie = 0;
int xfer_ld_q = 0;
u32 stat;
/* save and clear the status register */
stat = get_sr(chan);
set_sr(chan, stat);
dev_dbg(chan->dev, "irq: channel %d, stat = 0x%x\n", chan->id, stat);
chan_dbg(chan, "irq: stat = 0x%x\n", stat);
/* check that this was really our device */
stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH);
if (!stat)
return IRQ_NONE;
if (stat & FSL_DMA_SR_TE)
dev_err(chan->dev, "Transfer Error!\n");
chan_err(chan, "Transfer Error!\n");
/*
* Programming Error
@@ -1006,29 +1027,10 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
* triger a PE interrupt.
*/
if (stat & FSL_DMA_SR_PE) {
dev_dbg(chan->dev, "irq: Programming Error INT\n");
if (get_bcr(chan) == 0) {
/* BCR register is 0, this is a DMA_INTERRUPT async_tx.
* Now, update the completed cookie, and continue the
* next uncompleted transfer.
*/
update_cookie = 1;
xfer_ld_q = 1;
}
chan_dbg(chan, "irq: Programming Error INT\n");
stat &= ~FSL_DMA_SR_PE;
}
/*
* If the link descriptor segment transfer finishes,
* we will recycle the used descriptor.
*/
if (stat & FSL_DMA_SR_EOSI) {
dev_dbg(chan->dev, "irq: End-of-segments INT\n");
dev_dbg(chan->dev, "irq: clndar 0x%llx, nlndar 0x%llx\n",
(unsigned long long)get_cdar(chan),
(unsigned long long)get_ndar(chan));
stat &= ~FSL_DMA_SR_EOSI;
update_cookie = 1;
if (get_bcr(chan) != 0)
chan_err(chan, "Programming Error!\n");
}
/*
@@ -1036,10 +1038,8 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
* and start the next transfer if it exist.
*/
if (stat & FSL_DMA_SR_EOCDI) {
dev_dbg(chan->dev, "irq: End-of-Chain link INT\n");
chan_dbg(chan, "irq: End-of-Chain link INT\n");
stat &= ~FSL_DMA_SR_EOCDI;
update_cookie = 1;
xfer_ld_q = 1;
}
/*
@@ -1048,27 +1048,79 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
* prepare next transfer.
*/
if (stat & FSL_DMA_SR_EOLNI) {
dev_dbg(chan->dev, "irq: End-of-link INT\n");
chan_dbg(chan, "irq: End-of-link INT\n");
stat &= ~FSL_DMA_SR_EOLNI;
xfer_ld_q = 1;
}
if (update_cookie)
fsl_dma_update_completed_cookie(chan);
if (xfer_ld_q)
fsl_chan_xfer_ld_queue(chan);
if (stat)
dev_dbg(chan->dev, "irq: unhandled sr 0x%02x\n", stat);
/* check that the DMA controller is really idle */
if (!dma_is_idle(chan))
chan_err(chan, "irq: controller not idle!\n");
dev_dbg(chan->dev, "irq: Exit\n");
/* check that we handled all of the bits */
if (stat)
chan_err(chan, "irq: unhandled sr 0x%08x\n", stat);
/*
* Schedule the tasklet to handle all cleanup of the current
* transaction. It will start a new transaction if there is
* one pending.
*/
tasklet_schedule(&chan->tasklet);
chan_dbg(chan, "irq: Exit\n");
return IRQ_HANDLED;
}
static void dma_do_tasklet(unsigned long data)
{
struct fsldma_chan *chan = (struct fsldma_chan *)data;
fsl_chan_ld_cleanup(chan);
struct fsl_desc_sw *desc, *_desc;
LIST_HEAD(ld_cleanup);
unsigned long flags;
chan_dbg(chan, "tasklet entry\n");
spin_lock_irqsave(&chan->desc_lock, flags);
/* update the cookie if we have some descriptors to cleanup */
if (!list_empty(&chan->ld_running)) {
dma_cookie_t cookie;
desc = to_fsl_desc(chan->ld_running.prev);
cookie = desc->async_tx.cookie;
chan->completed_cookie = cookie;
chan_dbg(chan, "completed_cookie=%d\n", cookie);
}
/*
* move the descriptors to a temporary list so we can drop the lock
* during the entire cleanup operation
*/
list_splice_tail_init(&chan->ld_running, &ld_cleanup);
/* the hardware is now idle and ready for more */
chan->idle = true;
/*
* Start any pending transactions automatically
*
* In the ideal case, we keep the DMA controller busy while we go
* ahead and free the descriptors below.
*/
fsl_chan_xfer_ld_queue(chan);
spin_unlock_irqrestore(&chan->desc_lock, flags);
/* Run the callback for each descriptor, in order */
list_for_each_entry_safe(desc, _desc, &ld_cleanup, node) {
/* Remove from the list of transactions */
list_del(&desc->node);
/* Run all cleanup for this descriptor */
fsldma_cleanup_descriptor(chan, desc);
}
chan_dbg(chan, "tasklet exit\n");
}
static irqreturn_t fsldma_ctrl_irq(int irq, void *data)
@@ -1116,7 +1168,7 @@ static void fsldma_free_irqs(struct fsldma_device *fdev)
for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
chan = fdev->chan[i];
if (chan && chan->irq != NO_IRQ) {
dev_dbg(fdev->dev, "free channel %d IRQ\n", chan->id);
chan_dbg(chan, "free per-channel IRQ\n");
free_irq(chan->irq, chan);
}
}
@@ -1143,19 +1195,16 @@ static int fsldma_request_irqs(struct fsldma_device *fdev)
continue;
if (chan->irq == NO_IRQ) {
dev_err(fdev->dev, "no interrupts property defined for "
"DMA channel %d. Please fix your "
"device tree\n", chan->id);
chan_err(chan, "interrupts property missing in device tree\n");
ret = -ENODEV;
goto out_unwind;
}
dev_dbg(fdev->dev, "request channel %d IRQ\n", chan->id);
chan_dbg(chan, "request per-channel IRQ\n");
ret = request_irq(chan->irq, fsldma_chan_irq, IRQF_SHARED,
"fsldma-chan", chan);
if (ret) {
dev_err(fdev->dev, "unable to request IRQ for DMA "
"channel %d\n", chan->id);
chan_err(chan, "unable to request per-channel IRQ\n");
goto out_unwind;
}
}
@@ -1230,6 +1279,7 @@ static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev,
fdev->chan[chan->id] = chan;
tasklet_init(&chan->tasklet, dma_do_tasklet, (unsigned long)chan);
snprintf(chan->name, sizeof(chan->name), "chan%d", chan->id);
/* Initialize the channel */
dma_init(chan);
@@ -1250,6 +1300,7 @@ static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev,
spin_lock_init(&chan->desc_lock);
INIT_LIST_HEAD(&chan->ld_pending);
INIT_LIST_HEAD(&chan->ld_running);
chan->idle = true;
chan->common.device = &fdev->common;

View File

@@ -102,8 +102,8 @@ struct fsl_desc_sw {
} __attribute__((aligned(32)));
struct fsldma_chan_regs {
u32 mr; /* 0x00 - Mode Register */
u32 sr; /* 0x04 - Status Register */
u32 mr; /* 0x00 - Mode Register */
u32 sr; /* 0x04 - Status Register */
u64 cdar; /* 0x08 - Current descriptor address register */
u64 sar; /* 0x10 - Source Address Register */
u64 dar; /* 0x18 - Destination Address Register */
@@ -135,6 +135,7 @@ struct fsldma_device {
#define FSL_DMA_CHAN_START_EXT 0x00002000
struct fsldma_chan {
char name[8]; /* Channel name */
struct fsldma_chan_regs __iomem *regs;
dma_cookie_t completed_cookie; /* The maximum cookie completed */
spinlock_t desc_lock; /* Descriptor operation lock */
@@ -147,6 +148,7 @@ struct fsldma_chan {
int id; /* Raw id of this channel */
struct tasklet_struct tasklet;
u32 feature;
bool idle; /* DMA controller is idle */
void (*toggle_ext_pause)(struct fsldma_chan *fsl_chan, int enable);
void (*toggle_ext_start)(struct fsldma_chan *fsl_chan, int enable);

724
drivers/dma/mxs-dma.c Normal file
View File

@@ -0,0 +1,724 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
*
* Refer to drivers/dma/imx-sdma.c
*
* 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/init.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/mxs.h>
#include <mach/dma.h>
#include <mach/common.h>
/*
* NOTE: The term "PIO" throughout the mxs-dma implementation means
* PIO mode of mxs apbh-dma and apbx-dma. With this working mode,
* dma can program the controller registers of peripheral devices.
*/
#define MXS_DMA_APBH 0
#define MXS_DMA_APBX 1
#define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH)
#define APBH_VERSION_LATEST 3
#define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST)
#define HW_APBHX_CTRL0 0x000
#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8
#define BP_APBH_CTRL0_RESET_CHANNEL 16
#define HW_APBHX_CTRL1 0x010
#define HW_APBHX_CTRL2 0x020
#define HW_APBHX_CHANNEL_CTRL 0x030
#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16
#define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800)
#define HW_APBX_VERSION 0x800
#define BP_APBHX_VERSION_MAJOR 24
#define HW_APBHX_CHn_NXTCMDAR(n) \
(((dma_is_apbh() && apbh_is_old()) ? 0x050 : 0x110) + (n) * 0x70)
#define HW_APBHX_CHn_SEMA(n) \
(((dma_is_apbh() && apbh_is_old()) ? 0x080 : 0x140) + (n) * 0x70)
/*
* ccw bits definitions
*
* COMMAND: 0..1 (2)
* CHAIN: 2 (1)
* IRQ: 3 (1)
* NAND_LOCK: 4 (1) - not implemented
* NAND_WAIT4READY: 5 (1) - not implemented
* DEC_SEM: 6 (1)
* WAIT4END: 7 (1)
* HALT_ON_TERMINATE: 8 (1)
* TERMINATE_FLUSH: 9 (1)
* RESERVED: 10..11 (2)
* PIO_NUM: 12..15 (4)
*/
#define BP_CCW_COMMAND 0
#define BM_CCW_COMMAND (3 << 0)
#define CCW_CHAIN (1 << 2)
#define CCW_IRQ (1 << 3)
#define CCW_DEC_SEM (1 << 6)
#define CCW_WAIT4END (1 << 7)
#define CCW_HALT_ON_TERM (1 << 8)
#define CCW_TERM_FLUSH (1 << 9)
#define BP_CCW_PIO_NUM 12
#define BM_CCW_PIO_NUM (0xf << 12)
#define BF_CCW(value, field) (((value) << BP_CCW_##field) & BM_CCW_##field)
#define MXS_DMA_CMD_NO_XFER 0
#define MXS_DMA_CMD_WRITE 1
#define MXS_DMA_CMD_READ 2
#define MXS_DMA_CMD_DMA_SENSE 3 /* not implemented */
struct mxs_dma_ccw {
u32 next;
u16 bits;
u16 xfer_bytes;
#define MAX_XFER_BYTES 0xff00
u32 bufaddr;
#define MXS_PIO_WORDS 16
u32 pio_words[MXS_PIO_WORDS];
};
#define NUM_CCW (int)(PAGE_SIZE / sizeof(struct mxs_dma_ccw))
struct mxs_dma_chan {
struct mxs_dma_engine *mxs_dma;
struct dma_chan chan;
struct dma_async_tx_descriptor desc;
struct tasklet_struct tasklet;
int chan_irq;
struct mxs_dma_ccw *ccw;
dma_addr_t ccw_phys;
dma_cookie_t last_completed;
enum dma_status status;
unsigned int flags;
#define MXS_DMA_SG_LOOP (1 << 0)
};
#define MXS_DMA_CHANNELS 16
#define MXS_DMA_CHANNELS_MASK 0xffff
struct mxs_dma_engine {
int dev_id;
unsigned int version;
void __iomem *base;
struct clk *clk;
struct dma_device dma_device;
struct device_dma_parameters dma_parms;
struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
};
static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
if (dma_is_apbh() && apbh_is_old())
writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL),
mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
else
writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL),
mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
}
static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
/* set cmd_addr up */
writel(mxs_chan->ccw_phys,
mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id));
/* enable apbh channel clock */
if (dma_is_apbh()) {
if (apbh_is_old())
writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
else
writel(1 << chan_id,
mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
}
/* write 1 to SEMA to kick off the channel */
writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id));
}
static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
/* disable apbh channel clock */
if (dma_is_apbh()) {
if (apbh_is_old())
writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
else
writel(1 << chan_id,
mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
}
mxs_chan->status = DMA_SUCCESS;
}
static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
/* freeze the channel */
if (dma_is_apbh() && apbh_is_old())
writel(1 << chan_id,
mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
else
writel(1 << chan_id,
mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
mxs_chan->status = DMA_PAUSED;
}
static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan)
{
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_id = mxs_chan->chan.chan_id;
/* unfreeze the channel */
if (dma_is_apbh() && apbh_is_old())
writel(1 << chan_id,
mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
else
writel(1 << chan_id,
mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_CLR_ADDR);
mxs_chan->status = DMA_IN_PROGRESS;
}
static dma_cookie_t mxs_dma_assign_cookie(struct mxs_dma_chan *mxs_chan)
{
dma_cookie_t cookie = mxs_chan->chan.cookie;
if (++cookie < 0)
cookie = 1;
mxs_chan->chan.cookie = cookie;
mxs_chan->desc.cookie = cookie;
return cookie;
}
static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan)
{
return container_of(chan, struct mxs_dma_chan, chan);
}
static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(tx->chan);
mxs_dma_enable_chan(mxs_chan);
return mxs_dma_assign_cookie(mxs_chan);
}
static void mxs_dma_tasklet(unsigned long data)
{
struct mxs_dma_chan *mxs_chan = (struct mxs_dma_chan *) data;
if (mxs_chan->desc.callback)
mxs_chan->desc.callback(mxs_chan->desc.callback_param);
}
static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
{
struct mxs_dma_engine *mxs_dma = dev_id;
u32 stat1, stat2;
/* completion status */
stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1);
stat1 &= MXS_DMA_CHANNELS_MASK;
writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + MXS_CLR_ADDR);
/* error status */
stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2);
writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + MXS_CLR_ADDR);
/*
* When both completion and error of termination bits set at the
* same time, we do not take it as an error. IOW, it only becomes
* an error we need to handler here in case of ether it's (1) an bus
* error or (2) a termination error with no completion.
*/
stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) | /* (1) */
(~(stat2 >> MXS_DMA_CHANNELS) & stat2 & ~stat1); /* (2) */
/* combine error and completion status for checking */
stat1 = (stat2 << MXS_DMA_CHANNELS) | stat1;
while (stat1) {
int channel = fls(stat1) - 1;
struct mxs_dma_chan *mxs_chan =
&mxs_dma->mxs_chans[channel % MXS_DMA_CHANNELS];
if (channel >= MXS_DMA_CHANNELS) {
dev_dbg(mxs_dma->dma_device.dev,
"%s: error in channel %d\n", __func__,
channel - MXS_DMA_CHANNELS);
mxs_chan->status = DMA_ERROR;
mxs_dma_reset_chan(mxs_chan);
} else {
if (mxs_chan->flags & MXS_DMA_SG_LOOP)
mxs_chan->status = DMA_IN_PROGRESS;
else
mxs_chan->status = DMA_SUCCESS;
}
stat1 &= ~(1 << channel);
if (mxs_chan->status == DMA_SUCCESS)
mxs_chan->last_completed = mxs_chan->desc.cookie;
/* schedule tasklet on this channel */
tasklet_schedule(&mxs_chan->tasklet);
}
return IRQ_HANDLED;
}
static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_data *data = chan->private;
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int ret;
if (!data)
return -EINVAL;
mxs_chan->chan_irq = data->chan_irq;
mxs_chan->ccw = dma_alloc_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
&mxs_chan->ccw_phys, GFP_KERNEL);
if (!mxs_chan->ccw) {
ret = -ENOMEM;
goto err_alloc;
}
memset(mxs_chan->ccw, 0, PAGE_SIZE);
ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
0, "mxs-dma", mxs_dma);
if (ret)
goto err_irq;
ret = clk_enable(mxs_dma->clk);
if (ret)
goto err_clk;
mxs_dma_reset_chan(mxs_chan);
dma_async_tx_descriptor_init(&mxs_chan->desc, chan);
mxs_chan->desc.tx_submit = mxs_dma_tx_submit;
/* the descriptor is ready */
async_tx_ack(&mxs_chan->desc);
return 0;
err_clk:
free_irq(mxs_chan->chan_irq, mxs_dma);
err_irq:
dma_free_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
mxs_chan->ccw, mxs_chan->ccw_phys);
err_alloc:
return ret;
}
static void mxs_dma_free_chan_resources(struct dma_chan *chan)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
mxs_dma_disable_chan(mxs_chan);
free_irq(mxs_chan->chan_irq, mxs_dma);
dma_free_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
mxs_chan->ccw, mxs_chan->ccw_phys);
clk_disable(mxs_dma->clk);
}
static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_data_direction direction,
unsigned long append)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
struct mxs_dma_ccw *ccw;
struct scatterlist *sg;
int i, j;
u32 *pio;
static int idx;
if (mxs_chan->status == DMA_IN_PROGRESS && !append)
return NULL;
if (sg_len + (append ? idx : 0) > NUM_CCW) {
dev_err(mxs_dma->dma_device.dev,
"maximum number of sg exceeded: %d > %d\n",
sg_len, NUM_CCW);
goto err_out;
}
mxs_chan->status = DMA_IN_PROGRESS;
mxs_chan->flags = 0;
/*
* If the sg is prepared with append flag set, the sg
* will be appended to the last prepared sg.
*/
if (append) {
BUG_ON(idx < 1);
ccw = &mxs_chan->ccw[idx - 1];
ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
ccw->bits |= CCW_CHAIN;
ccw->bits &= ~CCW_IRQ;
ccw->bits &= ~CCW_DEC_SEM;
ccw->bits &= ~CCW_WAIT4END;
} else {
idx = 0;
}
if (direction == DMA_NONE) {
ccw = &mxs_chan->ccw[idx++];
pio = (u32 *) sgl;
for (j = 0; j < sg_len;)
ccw->pio_words[j++] = *pio++;
ccw->bits = 0;
ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_DEC_SEM;
ccw->bits |= CCW_WAIT4END;
ccw->bits |= CCW_HALT_ON_TERM;
ccw->bits |= CCW_TERM_FLUSH;
ccw->bits |= BF_CCW(sg_len, PIO_NUM);
ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND);
} else {
for_each_sg(sgl, sg, sg_len, i) {
if (sg->length > MAX_XFER_BYTES) {
dev_err(mxs_dma->dma_device.dev, "maximum bytes for sg entry exceeded: %d > %d\n",
sg->length, MAX_XFER_BYTES);
goto err_out;
}
ccw = &mxs_chan->ccw[idx++];
ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
ccw->bufaddr = sg->dma_address;
ccw->xfer_bytes = sg->length;
ccw->bits = 0;
ccw->bits |= CCW_CHAIN;
ccw->bits |= CCW_HALT_ON_TERM;
ccw->bits |= CCW_TERM_FLUSH;
ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ,
COMMAND);
if (i + 1 == sg_len) {
ccw->bits &= ~CCW_CHAIN;
ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_DEC_SEM;
ccw->bits |= CCW_WAIT4END;
}
}
}
return &mxs_chan->desc;
err_out:
mxs_chan->status = DMA_ERROR;
return NULL;
}
static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
size_t period_len, enum dma_data_direction direction)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int num_periods = buf_len / period_len;
int i = 0, buf = 0;
if (mxs_chan->status == DMA_IN_PROGRESS)
return NULL;
mxs_chan->status = DMA_IN_PROGRESS;
mxs_chan->flags |= MXS_DMA_SG_LOOP;
if (num_periods > NUM_CCW) {
dev_err(mxs_dma->dma_device.dev,
"maximum number of sg exceeded: %d > %d\n",
num_periods, NUM_CCW);
goto err_out;
}
if (period_len > MAX_XFER_BYTES) {
dev_err(mxs_dma->dma_device.dev,
"maximum period size exceeded: %d > %d\n",
period_len, MAX_XFER_BYTES);
goto err_out;
}
while (buf < buf_len) {
struct mxs_dma_ccw *ccw = &mxs_chan->ccw[i];
if (i + 1 == num_periods)
ccw->next = mxs_chan->ccw_phys;
else
ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * (i + 1);
ccw->bufaddr = dma_addr;
ccw->xfer_bytes = period_len;
ccw->bits = 0;
ccw->bits |= CCW_CHAIN;
ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_HALT_ON_TERM;
ccw->bits |= CCW_TERM_FLUSH;
ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND);
dma_addr += period_len;
buf += period_len;
i++;
}
return &mxs_chan->desc;
err_out:
mxs_chan->status = DMA_ERROR;
return NULL;
}
static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
int ret = 0;
switch (cmd) {
case DMA_TERMINATE_ALL:
mxs_dma_disable_chan(mxs_chan);
break;
case DMA_PAUSE:
mxs_dma_pause_chan(mxs_chan);
break;
case DMA_RESUME:
mxs_dma_resume_chan(mxs_chan);
break;
default:
ret = -ENOSYS;
}
return ret;
}
static enum dma_status mxs_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
dma_cookie_t last_used;
last_used = chan->cookie;
dma_set_tx_state(txstate, mxs_chan->last_completed, last_used, 0);
return mxs_chan->status;
}
static void mxs_dma_issue_pending(struct dma_chan *chan)
{
/*
* Nothing to do. We only have a single descriptor.
*/
}
static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma)
{
int ret;
ret = clk_enable(mxs_dma->clk);
if (ret)
goto err_out;
ret = mxs_reset_block(mxs_dma->base);
if (ret)
goto err_out;
/* only major version matters */
mxs_dma->version = readl(mxs_dma->base +
((mxs_dma->dev_id == MXS_DMA_APBX) ?
HW_APBX_VERSION : HW_APBH_VERSION)) >>
BP_APBHX_VERSION_MAJOR;
/* enable apbh burst */
if (dma_is_apbh()) {
writel(BM_APBH_CTRL0_APB_BURST_EN,
mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
writel(BM_APBH_CTRL0_APB_BURST8_EN,
mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
}
/* enable irq for all the channels */
writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS,
mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR);
clk_disable(mxs_dma->clk);
return 0;
err_out:
return ret;
}
static int __init mxs_dma_probe(struct platform_device *pdev)
{
const struct platform_device_id *id_entry =
platform_get_device_id(pdev);
struct mxs_dma_engine *mxs_dma;
struct resource *iores;
int ret, i;
mxs_dma = kzalloc(sizeof(*mxs_dma), GFP_KERNEL);
if (!mxs_dma)
return -ENOMEM;
mxs_dma->dev_id = id_entry->driver_data;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!request_mem_region(iores->start, resource_size(iores),
pdev->name)) {
ret = -EBUSY;
goto err_request_region;
}
mxs_dma->base = ioremap(iores->start, resource_size(iores));
if (!mxs_dma->base) {
ret = -ENOMEM;
goto err_ioremap;
}
mxs_dma->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(mxs_dma->clk)) {
ret = PTR_ERR(mxs_dma->clk);
goto err_clk;
}
dma_cap_set(DMA_SLAVE, mxs_dma->dma_device.cap_mask);
dma_cap_set(DMA_CYCLIC, mxs_dma->dma_device.cap_mask);
INIT_LIST_HEAD(&mxs_dma->dma_device.channels);
/* Initialize channel parameters */
for (i = 0; i < MXS_DMA_CHANNELS; i++) {
struct mxs_dma_chan *mxs_chan = &mxs_dma->mxs_chans[i];
mxs_chan->mxs_dma = mxs_dma;
mxs_chan->chan.device = &mxs_dma->dma_device;
tasklet_init(&mxs_chan->tasklet, mxs_dma_tasklet,
(unsigned long) mxs_chan);
/* Add the channel to mxs_chan list */
list_add_tail(&mxs_chan->chan.device_node,
&mxs_dma->dma_device.channels);
}
ret = mxs_dma_init(mxs_dma);
if (ret)
goto err_init;
mxs_dma->dma_device.dev = &pdev->dev;
/* mxs_dma gets 65535 bytes maximum sg size */
mxs_dma->dma_device.dev->dma_parms = &mxs_dma->dma_parms;
dma_set_max_seg_size(mxs_dma->dma_device.dev, MAX_XFER_BYTES);
mxs_dma->dma_device.device_alloc_chan_resources = mxs_dma_alloc_chan_resources;
mxs_dma->dma_device.device_free_chan_resources = mxs_dma_free_chan_resources;
mxs_dma->dma_device.device_tx_status = mxs_dma_tx_status;
mxs_dma->dma_device.device_prep_slave_sg = mxs_dma_prep_slave_sg;
mxs_dma->dma_device.device_prep_dma_cyclic = mxs_dma_prep_dma_cyclic;
mxs_dma->dma_device.device_control = mxs_dma_control;
mxs_dma->dma_device.device_issue_pending = mxs_dma_issue_pending;
ret = dma_async_device_register(&mxs_dma->dma_device);
if (ret) {
dev_err(mxs_dma->dma_device.dev, "unable to register\n");
goto err_init;
}
dev_info(mxs_dma->dma_device.dev, "initialized\n");
return 0;
err_init:
clk_put(mxs_dma->clk);
err_clk:
iounmap(mxs_dma->base);
err_ioremap:
release_mem_region(iores->start, resource_size(iores));
err_request_region:
kfree(mxs_dma);
return ret;
}
static struct platform_device_id mxs_dma_type[] = {
{
.name = "mxs-dma-apbh",
.driver_data = MXS_DMA_APBH,
}, {
.name = "mxs-dma-apbx",
.driver_data = MXS_DMA_APBX,
}
};
static struct platform_driver mxs_dma_driver = {
.driver = {
.name = "mxs-dma",
},
.id_table = mxs_dma_type,
};
static int __init mxs_dma_module_init(void)
{
return platform_driver_probe(&mxs_dma_driver, mxs_dma_probe);
}
subsys_initcall(mxs_dma_module_init);

View File

@@ -82,7 +82,7 @@ struct pch_dma_regs {
u32 dma_sts1;
u32 reserved2;
u32 reserved3;
struct pch_dma_desc_regs desc[0];
struct pch_dma_desc_regs desc[MAX_CHAN_NR];
};
struct pch_dma_desc {
@@ -124,7 +124,7 @@ struct pch_dma {
struct pci_pool *pool;
struct pch_dma_regs regs;
struct pch_dma_desc_regs ch_regs[MAX_CHAN_NR];
struct pch_dma_chan channels[0];
struct pch_dma_chan channels[MAX_CHAN_NR];
};
#define PCH_DMA_CTL0 0x00
@@ -366,7 +366,7 @@ static dma_cookie_t pd_tx_submit(struct dma_async_tx_descriptor *txd)
struct pch_dma_chan *pd_chan = to_pd_chan(txd->chan);
dma_cookie_t cookie;
spin_lock_bh(&pd_chan->lock);
spin_lock(&pd_chan->lock);
cookie = pdc_assign_cookie(pd_chan, desc);
if (list_empty(&pd_chan->active_list)) {
@@ -376,7 +376,7 @@ static dma_cookie_t pd_tx_submit(struct dma_async_tx_descriptor *txd)
list_add_tail(&desc->desc_node, &pd_chan->queue);
}
spin_unlock_bh(&pd_chan->lock);
spin_unlock(&pd_chan->lock);
return 0;
}
@@ -386,7 +386,7 @@ static struct pch_dma_desc *pdc_alloc_desc(struct dma_chan *chan, gfp_t flags)
struct pch_dma *pd = to_pd(chan->device);
dma_addr_t addr;
desc = pci_pool_alloc(pd->pool, GFP_KERNEL, &addr);
desc = pci_pool_alloc(pd->pool, flags, &addr);
if (desc) {
memset(desc, 0, sizeof(struct pch_dma_desc));
INIT_LIST_HEAD(&desc->tx_list);
@@ -405,7 +405,7 @@ static struct pch_dma_desc *pdc_desc_get(struct pch_dma_chan *pd_chan)
struct pch_dma_desc *ret = NULL;
int i;
spin_lock_bh(&pd_chan->lock);
spin_lock(&pd_chan->lock);
list_for_each_entry_safe(desc, _d, &pd_chan->free_list, desc_node) {
i++;
if (async_tx_test_ack(&desc->txd)) {
@@ -415,15 +415,15 @@ static struct pch_dma_desc *pdc_desc_get(struct pch_dma_chan *pd_chan)
}
dev_dbg(chan2dev(&pd_chan->chan), "desc %p not ACKed\n", desc);
}
spin_unlock_bh(&pd_chan->lock);
spin_unlock(&pd_chan->lock);
dev_dbg(chan2dev(&pd_chan->chan), "scanned %d descriptors\n", i);
if (!ret) {
ret = pdc_alloc_desc(&pd_chan->chan, GFP_NOIO);
if (ret) {
spin_lock_bh(&pd_chan->lock);
spin_lock(&pd_chan->lock);
pd_chan->descs_allocated++;
spin_unlock_bh(&pd_chan->lock);
spin_unlock(&pd_chan->lock);
} else {
dev_err(chan2dev(&pd_chan->chan),
"failed to alloc desc\n");
@@ -437,10 +437,10 @@ static void pdc_desc_put(struct pch_dma_chan *pd_chan,
struct pch_dma_desc *desc)
{
if (desc) {
spin_lock_bh(&pd_chan->lock);
spin_lock(&pd_chan->lock);
list_splice_init(&desc->tx_list, &pd_chan->free_list);
list_add(&desc->desc_node, &pd_chan->free_list);
spin_unlock_bh(&pd_chan->lock);
spin_unlock(&pd_chan->lock);
}
}
@@ -530,9 +530,9 @@ static void pd_issue_pending(struct dma_chan *chan)
struct pch_dma_chan *pd_chan = to_pd_chan(chan);
if (pdc_is_idle(pd_chan)) {
spin_lock_bh(&pd_chan->lock);
spin_lock(&pd_chan->lock);
pdc_advance_work(pd_chan);
spin_unlock_bh(&pd_chan->lock);
spin_unlock(&pd_chan->lock);
}
}
@@ -592,7 +592,6 @@ static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan,
goto err_desc_get;
}
if (!first) {
first = desc;
} else {
@@ -641,13 +640,13 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
spin_unlock_bh(&pd_chan->lock);
return 0;
}
static void pdc_tasklet(unsigned long data)
{
struct pch_dma_chan *pd_chan = (struct pch_dma_chan *)data;
unsigned long flags;
if (!pdc_is_idle(pd_chan)) {
dev_err(chan2dev(&pd_chan->chan),
@@ -655,12 +654,12 @@ static void pdc_tasklet(unsigned long data)
return;
}
spin_lock_bh(&pd_chan->lock);
spin_lock_irqsave(&pd_chan->lock, flags);
if (test_and_clear_bit(0, &pd_chan->err_status))
pdc_handle_error(pd_chan);
else
pdc_advance_work(pd_chan);
spin_unlock_bh(&pd_chan->lock);
spin_unlock_irqrestore(&pd_chan->lock, flags);
}
static irqreturn_t pd_irq(int irq, void *devid)
@@ -694,6 +693,7 @@ static irqreturn_t pd_irq(int irq, void *devid)
return ret;
}
#ifdef CONFIG_PM
static void pch_dma_save_regs(struct pch_dma *pd)
{
struct pch_dma_chan *pd_chan;
@@ -771,6 +771,7 @@ static int pch_dma_resume(struct pci_dev *pdev)
return 0;
}
#endif
static int __devinit pch_dma_probe(struct pci_dev *pdev,
const struct pci_device_id *id)

File diff suppressed because it is too large Load Diff

View File

@@ -125,13 +125,15 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
static int d40_phy_fill_lli(struct d40_phy_lli *lli,
dma_addr_t data,
u32 data_size,
int psize,
dma_addr_t next_lli,
u32 reg_cfg,
bool term_int,
u32 data_width,
bool is_device)
struct stedma40_half_channel_info *info,
unsigned int flags)
{
bool addr_inc = flags & LLI_ADDR_INC;
bool term_int = flags & LLI_TERM_INT;
unsigned int data_width = info->data_width;
int psize = info->psize;
int num_elems;
if (psize == STEDMA40_PSIZE_PHY_1)
@@ -154,7 +156,7 @@ static int d40_phy_fill_lli(struct d40_phy_lli *lli,
* Distance to next element sized entry.
* Usually the size of the element unless you want gaps.
*/
if (!is_device)
if (addr_inc)
lli->reg_elt |= (0x1 << data_width) <<
D40_SREG_ELEM_PHY_EIDX_POS;
@@ -198,47 +200,51 @@ static int d40_seg_size(int size, int data_width1, int data_width2)
return seg_max;
}
struct d40_phy_lli *d40_phy_buf_to_lli(struct d40_phy_lli *lli,
dma_addr_t addr,
u32 size,
int psize,
dma_addr_t lli_phys,
u32 reg_cfg,
bool term_int,
u32 data_width1,
u32 data_width2,
bool is_device)
static struct d40_phy_lli *
d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size,
dma_addr_t lli_phys, dma_addr_t first_phys, u32 reg_cfg,
struct stedma40_half_channel_info *info,
struct stedma40_half_channel_info *otherinfo,
unsigned long flags)
{
bool lastlink = flags & LLI_LAST_LINK;
bool addr_inc = flags & LLI_ADDR_INC;
bool term_int = flags & LLI_TERM_INT;
bool cyclic = flags & LLI_CYCLIC;
int err;
dma_addr_t next = lli_phys;
int size_rest = size;
int size_seg = 0;
/*
* This piece may be split up based on d40_seg_size(); we only want the
* term int on the last part.
*/
if (term_int)
flags &= ~LLI_TERM_INT;
do {
size_seg = d40_seg_size(size_rest, data_width1, data_width2);
size_seg = d40_seg_size(size_rest, info->data_width,
otherinfo->data_width);
size_rest -= size_seg;
if (term_int && size_rest == 0)
next = 0;
if (size_rest == 0 && term_int)
flags |= LLI_TERM_INT;
if (size_rest == 0 && lastlink)
next = cyclic ? first_phys : 0;
else
next = ALIGN(next + sizeof(struct d40_phy_lli),
D40_LLI_ALIGN);
err = d40_phy_fill_lli(lli,
addr,
size_seg,
psize,
next,
reg_cfg,
!next,
data_width1,
is_device);
err = d40_phy_fill_lli(lli, addr, size_seg, next,
reg_cfg, info, flags);
if (err)
goto err;
lli++;
if (!is_device)
if (addr_inc)
addr += size_seg;
} while (size_rest);
@@ -254,39 +260,35 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
struct d40_phy_lli *lli_sg,
dma_addr_t lli_phys,
u32 reg_cfg,
u32 data_width1,
u32 data_width2,
int psize)
struct stedma40_half_channel_info *info,
struct stedma40_half_channel_info *otherinfo,
unsigned long flags)
{
int total_size = 0;
int i;
struct scatterlist *current_sg = sg;
dma_addr_t dst;
struct d40_phy_lli *lli = lli_sg;
dma_addr_t l_phys = lli_phys;
if (!target)
flags |= LLI_ADDR_INC;
for_each_sg(sg, current_sg, sg_len, i) {
dma_addr_t sg_addr = sg_dma_address(current_sg);
unsigned int len = sg_dma_len(current_sg);
dma_addr_t dst = target ?: sg_addr;
total_size += sg_dma_len(current_sg);
if (target)
dst = target;
else
dst = sg_phys(current_sg);
if (i == sg_len - 1)
flags |= LLI_TERM_INT | LLI_LAST_LINK;
l_phys = ALIGN(lli_phys + (lli - lli_sg) *
sizeof(struct d40_phy_lli), D40_LLI_ALIGN);
lli = d40_phy_buf_to_lli(lli,
dst,
sg_dma_len(current_sg),
psize,
l_phys,
reg_cfg,
sg_len - 1 == i,
data_width1,
data_width2,
target == dst);
lli = d40_phy_buf_to_lli(lli, dst, len, l_phys, lli_phys,
reg_cfg, info, otherinfo, flags);
if (lli == NULL)
return -EINVAL;
}
@@ -295,45 +297,22 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
}
void d40_phy_lli_write(void __iomem *virtbase,
u32 phy_chan_num,
struct d40_phy_lli *lli_dst,
struct d40_phy_lli *lli_src)
{
writel(lli_src->reg_cfg, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSCFG);
writel(lli_src->reg_elt, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSELT);
writel(lli_src->reg_ptr, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSPTR);
writel(lli_src->reg_lnk, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSLNK);
writel(lli_dst->reg_cfg, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDCFG);
writel(lli_dst->reg_elt, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDELT);
writel(lli_dst->reg_ptr, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDPTR);
writel(lli_dst->reg_lnk, virtbase + D40_DREG_PCBASE +
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDLNK);
}
/* DMA logical lli operations */
static void d40_log_lli_link(struct d40_log_lli *lli_dst,
struct d40_log_lli *lli_src,
int next)
int next, unsigned int flags)
{
bool interrupt = flags & LLI_TERM_INT;
u32 slos = 0;
u32 dlos = 0;
if (next != -EINVAL) {
slos = next * 2;
dlos = next * 2 + 1;
} else {
}
if (interrupt) {
lli_dst->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK;
lli_dst->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK;
}
@@ -348,9 +327,9 @@ static void d40_log_lli_link(struct d40_log_lli *lli_dst,
void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
struct d40_log_lli *lli_dst,
struct d40_log_lli *lli_src,
int next)
int next, unsigned int flags)
{
d40_log_lli_link(lli_dst, lli_src, next);
d40_log_lli_link(lli_dst, lli_src, next, flags);
writel(lli_src->lcsp02, &lcpa[0].lcsp0);
writel(lli_src->lcsp13, &lcpa[0].lcsp1);
@@ -361,9 +340,9 @@ void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
struct d40_log_lli *lli_dst,
struct d40_log_lli *lli_src,
int next)
int next, unsigned int flags)
{
d40_log_lli_link(lli_dst, lli_src, next);
d40_log_lli_link(lli_dst, lli_src, next, flags);
writel(lli_src->lcsp02, &lcla[0].lcsp02);
writel(lli_src->lcsp13, &lcla[0].lcsp13);
@@ -375,8 +354,10 @@ static void d40_log_fill_lli(struct d40_log_lli *lli,
dma_addr_t data, u32 data_size,
u32 reg_cfg,
u32 data_width,
bool addr_inc)
unsigned int flags)
{
bool addr_inc = flags & LLI_ADDR_INC;
lli->lcsp13 = reg_cfg;
/* The number of elements to transfer */
@@ -395,67 +376,15 @@ static void d40_log_fill_lli(struct d40_log_lli *lli,
}
int d40_log_sg_to_dev(struct scatterlist *sg,
int sg_len,
struct d40_log_lli_bidir *lli,
struct d40_def_lcsp *lcsp,
u32 src_data_width,
u32 dst_data_width,
enum dma_data_direction direction,
dma_addr_t dev_addr)
{
int total_size = 0;
struct scatterlist *current_sg = sg;
int i;
struct d40_log_lli *lli_src = lli->src;
struct d40_log_lli *lli_dst = lli->dst;
for_each_sg(sg, current_sg, sg_len, i) {
total_size += sg_dma_len(current_sg);
if (direction == DMA_TO_DEVICE) {
lli_src =
d40_log_buf_to_lli(lli_src,
sg_phys(current_sg),
sg_dma_len(current_sg),
lcsp->lcsp1, src_data_width,
dst_data_width,
true);
lli_dst =
d40_log_buf_to_lli(lli_dst,
dev_addr,
sg_dma_len(current_sg),
lcsp->lcsp3, dst_data_width,
src_data_width,
false);
} else {
lli_dst =
d40_log_buf_to_lli(lli_dst,
sg_phys(current_sg),
sg_dma_len(current_sg),
lcsp->lcsp3, dst_data_width,
src_data_width,
true);
lli_src =
d40_log_buf_to_lli(lli_src,
dev_addr,
sg_dma_len(current_sg),
lcsp->lcsp1, src_data_width,
dst_data_width,
false);
}
}
return total_size;
}
struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
static struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
dma_addr_t addr,
int size,
u32 lcsp13, /* src or dst*/
u32 data_width1,
u32 data_width2,
bool addr_inc)
unsigned int flags)
{
bool addr_inc = flags & LLI_ADDR_INC;
struct d40_log_lli *lli = lli_sg;
int size_rest = size;
int size_seg = 0;
@@ -468,7 +397,7 @@ struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
addr,
size_seg,
lcsp13, data_width1,
addr_inc);
flags);
if (addr_inc)
addr += size_seg;
lli++;
@@ -479,6 +408,7 @@ struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
int d40_log_sg_to_lli(struct scatterlist *sg,
int sg_len,
dma_addr_t dev_addr,
struct d40_log_lli *lli_sg,
u32 lcsp13, /* src or dst*/
u32 data_width1, u32 data_width2)
@@ -487,14 +417,24 @@ int d40_log_sg_to_lli(struct scatterlist *sg,
struct scatterlist *current_sg = sg;
int i;
struct d40_log_lli *lli = lli_sg;
unsigned long flags = 0;
if (!dev_addr)
flags |= LLI_ADDR_INC;
for_each_sg(sg, current_sg, sg_len, i) {
dma_addr_t sg_addr = sg_dma_address(current_sg);
unsigned int len = sg_dma_len(current_sg);
dma_addr_t addr = dev_addr ?: sg_addr;
total_size += sg_dma_len(current_sg);
lli = d40_log_buf_to_lli(lli,
sg_phys(current_sg),
sg_dma_len(current_sg),
lli = d40_log_buf_to_lli(lli, addr, len,
lcsp13,
data_width1, data_width2, true);
data_width1,
data_width2,
flags);
}
return total_size;
}

View File

@@ -163,6 +163,22 @@
#define D40_DREG_LCEIS1 0x0B4
#define D40_DREG_LCEIS2 0x0B8
#define D40_DREG_LCEIS3 0x0BC
#define D40_DREG_PSEG1 0x110
#define D40_DREG_PSEG2 0x114
#define D40_DREG_PSEG3 0x118
#define D40_DREG_PSEG4 0x11C
#define D40_DREG_PCEG1 0x120
#define D40_DREG_PCEG2 0x124
#define D40_DREG_PCEG3 0x128
#define D40_DREG_PCEG4 0x12C
#define D40_DREG_RSEG1 0x130
#define D40_DREG_RSEG2 0x134
#define D40_DREG_RSEG3 0x138
#define D40_DREG_RSEG4 0x13C
#define D40_DREG_RCEG1 0x140
#define D40_DREG_RCEG2 0x144
#define D40_DREG_RCEG3 0x148
#define D40_DREG_RCEG4 0x14C
#define D40_DREG_STFU 0xFC8
#define D40_DREG_ICFG 0xFCC
#define D40_DREG_PERIPHID0 0xFE0
@@ -277,6 +293,13 @@ struct d40_def_lcsp {
/* Physical channels */
enum d40_lli_flags {
LLI_ADDR_INC = 1 << 0,
LLI_TERM_INT = 1 << 1,
LLI_CYCLIC = 1 << 2,
LLI_LAST_LINK = 1 << 3,
};
void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
u32 *src_cfg,
u32 *dst_cfg,
@@ -292,46 +315,15 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
struct d40_phy_lli *lli,
dma_addr_t lli_phys,
u32 reg_cfg,
u32 data_width1,
u32 data_width2,
int psize);
struct d40_phy_lli *d40_phy_buf_to_lli(struct d40_phy_lli *lli,
dma_addr_t data,
u32 data_size,
int psize,
dma_addr_t next_lli,
u32 reg_cfg,
bool term_int,
u32 data_width1,
u32 data_width2,
bool is_device);
void d40_phy_lli_write(void __iomem *virtbase,
u32 phy_chan_num,
struct d40_phy_lli *lli_dst,
struct d40_phy_lli *lli_src);
struct stedma40_half_channel_info *info,
struct stedma40_half_channel_info *otherinfo,
unsigned long flags);
/* Logical channels */
struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
dma_addr_t addr,
int size,
u32 lcsp13, /* src or dst*/
u32 data_width1, u32 data_width2,
bool addr_inc);
int d40_log_sg_to_dev(struct scatterlist *sg,
int sg_len,
struct d40_log_lli_bidir *lli,
struct d40_def_lcsp *lcsp,
u32 src_data_width,
u32 dst_data_width,
enum dma_data_direction direction,
dma_addr_t dev_addr);
int d40_log_sg_to_lli(struct scatterlist *sg,
int sg_len,
dma_addr_t dev_addr,
struct d40_log_lli *lli_sg,
u32 lcsp13, /* src or dst*/
u32 data_width1, u32 data_width2);
@@ -339,11 +331,11 @@ int d40_log_sg_to_lli(struct scatterlist *sg,
void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
struct d40_log_lli *lli_dst,
struct d40_log_lli *lli_src,
int next);
int next, unsigned int flags);
void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
struct d40_log_lli *lli_dst,
struct d40_log_lli *lli_src,
int next);
int next, unsigned int flags);
#endif /* STE_DMA40_LLI_H */

View File

@@ -145,4 +145,16 @@ config ISCSI_IBFT
detect iSCSI boot parameters dynamically during system boot, say Y.
Otherwise, say N.
config SIGMA
tristate "SigmaStudio firmware loader"
depends on I2C
select CRC32
default n
help
Enable helper functions for working with Analog Devices SigmaDSP
parts and binary firmwares produced by Analog Devices SigmaStudio.
If unsure, say N here. Drivers that need these helpers will select
this option automatically.
endmenu

View File

@@ -12,3 +12,4 @@ obj-$(CONFIG_DMIID) += dmi-id.o
obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_SIGMA) += sigma.o

115
drivers/firmware/sigma.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/sigma.h>
/* Return: 0==OK, <0==error, =1 ==no more actions */
static int
process_sigma_action(struct i2c_client *client, struct sigma_firmware *ssfw)
{
struct sigma_action *sa = (void *)(ssfw->fw->data + ssfw->pos);
size_t len = sigma_action_len(sa);
int ret = 0;
pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
sa->instr, sa->addr, len);
switch (sa->instr) {
case SIGMA_ACTION_WRITEXBYTES:
case SIGMA_ACTION_WRITESINGLE:
case SIGMA_ACTION_WRITESAFELOAD:
if (ssfw->fw->size < ssfw->pos + len)
return -EINVAL;
ret = i2c_master_send(client, (void *)&sa->addr, len);
if (ret < 0)
return -EINVAL;
break;
case SIGMA_ACTION_DELAY:
ret = 0;
udelay(len);
len = 0;
break;
case SIGMA_ACTION_END:
return 1;
default:
return -EINVAL;
}
/* when arrive here ret=0 or sent data */
ssfw->pos += sigma_action_size(sa, len);
return ssfw->pos == ssfw->fw->size;
}
static int
process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
{
pr_debug("%s: processing %p\n", __func__, ssfw);
while (1) {
int ret = process_sigma_action(client, ssfw);
pr_debug("%s: action returned %i\n", __func__, ret);
if (ret == 1)
return 0;
else if (ret)
return ret;
}
}
int process_sigma_firmware(struct i2c_client *client, const char *name)
{
int ret;
struct sigma_firmware_header *ssfw_head;
struct sigma_firmware ssfw;
const struct firmware *fw;
u32 crc;
pr_debug("%s: loading firmware %s\n", __func__, name);
/* first load the blob */
ret = request_firmware(&fw, name, &client->dev);
if (ret) {
pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
return ret;
}
ssfw.fw = fw;
/* then verify the header */
ret = -EINVAL;
if (fw->size < sizeof(*ssfw_head))
goto done;
ssfw_head = (void *)fw->data;
if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic)))
goto done;
crc = crc32(0, fw->data, fw->size);
pr_debug("%s: crc=%x\n", __func__, crc);
if (crc != ssfw_head->crc)
goto done;
ssfw.pos = sizeof(*ssfw_head);
/* finally process all of the actions */
ret = process_sigma_actions(client, &ssfw);
done:
release_firmware(fw);
pr_debug("%s: loaded %s\n", __func__, name);
return ret;
}
EXPORT_SYMBOL(process_sigma_firmware);

View File

@@ -88,18 +88,20 @@ static const struct backlight_ops nv50_bl_ops = {
.update_status = nv50_set_intensity,
};
static int nouveau_nv40_backlight_init(struct drm_device *dev)
static int nouveau_nv40_backlight_init(struct drm_connector *connector)
{
struct backlight_properties props;
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct backlight_properties props;
struct backlight_device *bd;
if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
return 0;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = 31;
bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
bd = backlight_device_register("nv_backlight", &connector->kdev, dev,
&nv40_bl_ops, &props);
if (IS_ERR(bd))
return PTR_ERR(bd);
@@ -111,18 +113,20 @@ static int nouveau_nv40_backlight_init(struct drm_device *dev)
return 0;
}
static int nouveau_nv50_backlight_init(struct drm_device *dev)
static int nouveau_nv50_backlight_init(struct drm_connector *connector)
{
struct backlight_properties props;
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct backlight_properties props;
struct backlight_device *bd;
if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
return 0;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = 1025;
bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
bd = backlight_device_register("nv_backlight", &connector->kdev, dev,
&nv50_bl_ops, &props);
if (IS_ERR(bd))
return PTR_ERR(bd);
@@ -133,8 +137,9 @@ static int nouveau_nv50_backlight_init(struct drm_device *dev)
return 0;
}
int nouveau_backlight_init(struct drm_device *dev)
int nouveau_backlight_init(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
#ifdef CONFIG_ACPI
@@ -147,9 +152,9 @@ int nouveau_backlight_init(struct drm_device *dev)
switch (dev_priv->card_type) {
case NV_40:
return nouveau_nv40_backlight_init(dev);
return nouveau_nv40_backlight_init(connector);
case NV_50:
return nouveau_nv50_backlight_init(dev);
return nouveau_nv50_backlight_init(connector);
default:
break;
}
@@ -157,8 +162,9 @@ int nouveau_backlight_init(struct drm_device *dev)
return 0;
}
void nouveau_backlight_exit(struct drm_device *dev)
void nouveau_backlight_exit(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->backlight) {

View File

@@ -116,6 +116,10 @@ nouveau_connector_destroy(struct drm_connector *connector)
nouveau_connector_hotplug, connector);
}
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
nouveau_backlight_exit(connector);
kfree(nv_connector->edid);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
@@ -894,6 +898,11 @@ nouveau_connector_create(struct drm_device *dev, int index)
}
drm_sysfs_connector_add(connector);
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
nouveau_backlight_init(connector);
dcb->drm = connector;
return dcb->drm;

View File

@@ -999,15 +999,15 @@ static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector
/* nouveau_backlight.c */
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
extern int nouveau_backlight_init(struct drm_device *);
extern void nouveau_backlight_exit(struct drm_device *);
extern int nouveau_backlight_init(struct drm_connector *);
extern void nouveau_backlight_exit(struct drm_connector *);
#else
static inline int nouveau_backlight_init(struct drm_device *dev)
static inline int nouveau_backlight_init(struct drm_connector *dev)
{
return 0;
}
static inline void nouveau_backlight_exit(struct drm_device *dev) { }
static inline void nouveau_backlight_exit(struct drm_connector *dev) { }
#endif
/* nouveau_bios.c */

View File

@@ -704,10 +704,6 @@ nouveau_card_init(struct drm_device *dev)
goto out_fence;
}
ret = nouveau_backlight_init(dev);
if (ret)
NV_ERROR(dev, "Error %d registering backlight\n", ret);
nouveau_fbcon_init(dev);
drm_kms_helper_poll_init(dev);
return 0;
@@ -759,8 +755,6 @@ static void nouveau_card_takedown(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
nouveau_backlight_exit(dev);
if (!engine->graph.accel_blocked) {
nouveau_fence_fini(dev);
nouveau_channel_put_unlocked(&dev_priv->channel);

View File

@@ -1,6 +1,7 @@
config DRM_RADEON_KMS
bool "Enable modesetting on radeon by default - NEW DRIVER"
depends on DRM_RADEON
select BACKLIGHT_CLASS_DEVICE
help
Choose this option if you want kernel modesetting enabled by default.

View File

@@ -40,6 +40,10 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
struct drm_encoder *encoder,
bool connected);
extern void
radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder,
struct drm_connector *drm_connector);
void radeon_connector_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -1526,6 +1530,17 @@ radeon_add_legacy_connector(struct drm_device *dev,
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->display_info.subpixel_order = subpixel_order;
drm_sysfs_connector_add(connector);
if (connector_type == DRM_MODE_CONNECTOR_LVDS) {
struct drm_encoder *drm_encoder;
list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
struct radeon_encoder *radeon_encoder;
radeon_encoder = to_radeon_encoder(drm_encoder);
if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_LVDS)
radeon_legacy_backlight_init(radeon_encoder, connector);
}
}
return;
failed:

View File

@@ -28,6 +28,10 @@
#include "radeon_drm.h"
#include "radeon.h"
#include "atom.h"
#include <linux/backlight.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
static void radeon_legacy_encoder_disable(struct drm_encoder *encoder)
{
@@ -39,7 +43,7 @@ static void radeon_legacy_encoder_disable(struct drm_encoder *encoder)
radeon_encoder->active_device = 0;
}
static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
static void radeon_legacy_lvds_update(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -47,15 +51,23 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
uint32_t lvds_gen_cntl, lvds_pll_cntl, pixclks_cntl, disp_pwr_man;
int panel_pwr_delay = 2000;
bool is_mac = false;
uint8_t backlight_level;
DRM_DEBUG_KMS("\n");
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
backlight_level = (lvds_gen_cntl >> RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff;
if (radeon_encoder->enc_priv) {
if (rdev->is_atom_bios) {
struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv;
panel_pwr_delay = lvds->panel_pwr_delay;
if (lvds->bl_dev)
backlight_level = lvds->backlight_level;
} else {
struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv;
panel_pwr_delay = lvds->panel_pwr_delay;
if (lvds->bl_dev)
backlight_level = lvds->backlight_level;
}
}
@@ -82,11 +94,13 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET;
WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl);
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN | RADEON_LVDS_DIGON | RADEON_LVDS_BLON);
lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS |
RADEON_LVDS_BL_MOD_LEVEL_MASK);
lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN |
RADEON_LVDS_DIGON | RADEON_LVDS_BLON |
(backlight_level << RADEON_LVDS_BL_MOD_LEVEL_SHIFT));
if (is_mac)
lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN;
lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS);
udelay(panel_pwr_delay * 1000);
WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
break;
@@ -95,7 +109,6 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
case DRM_MODE_DPMS_OFF:
pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL);
WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb);
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
if (is_mac) {
lvds_gen_cntl &= ~RADEON_LVDS_BL_MOD_EN;
@@ -119,6 +132,25 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
}
static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
{
struct radeon_device *rdev = encoder->dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
DRM_DEBUG("\n");
if (radeon_encoder->enc_priv) {
if (rdev->is_atom_bios) {
struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv;
lvds->dpms_mode = mode;
} else {
struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv;
lvds->dpms_mode = mode;
}
}
radeon_legacy_lvds_update(encoder, mode);
}
static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder)
{
struct radeon_device *rdev = encoder->dev->dev_private;
@@ -237,9 +269,222 @@ static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = {
.disable = radeon_legacy_encoder_disable,
};
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
#define MAX_RADEON_LEVEL 0xFF
struct radeon_backlight_privdata {
struct radeon_encoder *encoder;
uint8_t negative;
};
static uint8_t radeon_legacy_lvds_level(struct backlight_device *bd)
{
struct radeon_backlight_privdata *pdata = bl_get_data(bd);
uint8_t level;
/* Convert brightness to hardware level */
if (bd->props.brightness < 0)
level = 0;
else if (bd->props.brightness > MAX_RADEON_LEVEL)
level = MAX_RADEON_LEVEL;
else
level = bd->props.brightness;
if (pdata->negative)
level = MAX_RADEON_LEVEL - level;
return level;
}
static int radeon_legacy_backlight_update_status(struct backlight_device *bd)
{
struct radeon_backlight_privdata *pdata = bl_get_data(bd);
struct radeon_encoder *radeon_encoder = pdata->encoder;
struct drm_device *dev = radeon_encoder->base.dev;
struct radeon_device *rdev = dev->dev_private;
int dpms_mode = DRM_MODE_DPMS_ON;
if (radeon_encoder->enc_priv) {
if (rdev->is_atom_bios) {
struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv;
dpms_mode = lvds->dpms_mode;
lvds->backlight_level = radeon_legacy_lvds_level(bd);
} else {
struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv;
dpms_mode = lvds->dpms_mode;
lvds->backlight_level = radeon_legacy_lvds_level(bd);
}
}
if (bd->props.brightness > 0)
radeon_legacy_lvds_update(&radeon_encoder->base, dpms_mode);
else
radeon_legacy_lvds_update(&radeon_encoder->base, DRM_MODE_DPMS_OFF);
return 0;
}
static int radeon_legacy_backlight_get_brightness(struct backlight_device *bd)
{
struct radeon_backlight_privdata *pdata = bl_get_data(bd);
struct radeon_encoder *radeon_encoder = pdata->encoder;
struct drm_device *dev = radeon_encoder->base.dev;
struct radeon_device *rdev = dev->dev_private;
uint8_t backlight_level;
backlight_level = (RREG32(RADEON_LVDS_GEN_CNTL) >>
RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff;
return pdata->negative ? MAX_RADEON_LEVEL - backlight_level : backlight_level;
}
static const struct backlight_ops radeon_backlight_ops = {
.get_brightness = radeon_legacy_backlight_get_brightness,
.update_status = radeon_legacy_backlight_update_status,
};
void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder,
struct drm_connector *drm_connector)
{
struct drm_device *dev = radeon_encoder->base.dev;
struct radeon_device *rdev = dev->dev_private;
struct backlight_device *bd;
struct backlight_properties props;
struct radeon_backlight_privdata *pdata;
uint8_t backlight_level;
if (!radeon_encoder->enc_priv)
return;
#ifdef CONFIG_PMAC_BACKLIGHT
if (!pmac_has_backlight_type("ati") &&
!pmac_has_backlight_type("mnca"))
return;
#endif
pdata = kmalloc(sizeof(struct radeon_backlight_privdata), GFP_KERNEL);
if (!pdata) {
DRM_ERROR("Memory allocation failed\n");
goto error;
}
props.max_brightness = MAX_RADEON_LEVEL;
props.type = BACKLIGHT_RAW;
bd = backlight_device_register("radeon_bl", &drm_connector->kdev,
pdata, &radeon_backlight_ops, &props);
if (IS_ERR(bd)) {
DRM_ERROR("Backlight registration failed\n");
goto error;
}
pdata->encoder = radeon_encoder;
backlight_level = (RREG32(RADEON_LVDS_GEN_CNTL) >>
RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff;
/* First, try to detect backlight level sense based on the assumption
* that firmware set it up at full brightness
*/
if (backlight_level == 0)
pdata->negative = true;
else if (backlight_level == 0xff)
pdata->negative = false;
else {
/* XXX hack... maybe some day we can figure out in what direction
* backlight should work on a given panel?
*/
pdata->negative = (rdev->family != CHIP_RV200 &&
rdev->family != CHIP_RV250 &&
rdev->family != CHIP_RV280 &&
rdev->family != CHIP_RV350);
#ifdef CONFIG_PMAC_BACKLIGHT
pdata->negative = (pdata->negative ||
of_machine_is_compatible("PowerBook4,3") ||
of_machine_is_compatible("PowerBook6,3") ||
of_machine_is_compatible("PowerBook6,5"));
#endif
}
if (rdev->is_atom_bios) {
struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv;
lvds->bl_dev = bd;
} else {
struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv;
lvds->bl_dev = bd;
}
bd->props.brightness = radeon_legacy_backlight_get_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK;
backlight_update_status(bd);
DRM_INFO("radeon legacy LVDS backlight initialized\n");
return;
error:
kfree(pdata);
return;
}
static void radeon_legacy_backlight_exit(struct radeon_encoder *radeon_encoder)
{
struct drm_device *dev = radeon_encoder->base.dev;
struct radeon_device *rdev = dev->dev_private;
struct backlight_device *bd = NULL;
if (!radeon_encoder->enc_priv)
return;
if (rdev->is_atom_bios) {
struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv;
bd = lvds->bl_dev;
lvds->bl_dev = NULL;
} else {
struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv;
bd = lvds->bl_dev;
lvds->bl_dev = NULL;
}
if (bd) {
struct radeon_legacy_backlight_privdata *pdata;
pdata = bl_get_data(bd);
backlight_device_unregister(bd);
kfree(pdata);
DRM_INFO("radeon legacy LVDS backlight unloaded\n");
}
}
#else /* !CONFIG_BACKLIGHT_CLASS_DEVICE */
void radeon_legacy_backlight_init(struct radeon_encoder *encoder)
{
}
static void radeon_legacy_backlight_exit(struct radeon_encoder *encoder)
{
}
#endif
static void radeon_lvds_enc_destroy(struct drm_encoder *encoder)
{
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
if (radeon_encoder->enc_priv) {
radeon_legacy_backlight_exit(radeon_encoder);
kfree(radeon_encoder->enc_priv);
}
drm_encoder_cleanup(encoder);
kfree(radeon_encoder);
}
static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = {
.destroy = radeon_enc_destroy,
.destroy = radeon_lvds_enc_destroy,
};
static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode)

View File

@@ -302,6 +302,9 @@ struct radeon_encoder_lvds {
uint32_t lvds_gen_cntl;
/* panel mode */
struct drm_display_mode native_mode;
struct backlight_device *bl_dev;
int dpms_mode;
uint8_t backlight_level;
};
struct radeon_encoder_tv_dac {
@@ -355,6 +358,9 @@ struct radeon_encoder_atom_dig {
uint32_t lcd_ss_id;
/* panel mode */
struct drm_display_mode native_mode;
struct backlight_device *bl_dev;
int dpms_mode;
uint8_t backlight_level;
};
struct radeon_encoder_atom_dac {

View File

@@ -944,6 +944,7 @@ static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *
}
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
bdev = backlight_device_register(dev_name(dev), dev, data,
&picolcd_blops, &props);

View File

@@ -547,15 +547,18 @@ config I2C_PUV3
config I2C_PXA
tristate "Intel PXA2XX I2C adapter"
depends on ARCH_PXA || ARCH_MMP
depends on ARCH_PXA || ARCH_MMP || (X86_32 && PCI && OF)
help
If you have devices in the PXA I2C bus, say yes to this option.
This driver can also be built as a module. If so, the module
will be called i2c-pxa.
config I2C_PXA_PCI
def_bool I2C_PXA && X86_32 && PCI && OF
config I2C_PXA_SLAVE
bool "Intel PXA2XX I2C Slave comms support"
depends on I2C_PXA
depends on I2C_PXA && !X86_32
help
Support I2C slave mode communications on the PXA I2C bus. This
is necessary for systems where the PXA may be a target on the
@@ -668,15 +671,28 @@ config I2C_XILINX
will be called xilinx_i2c.
config I2C_EG20T
tristate "PCH I2C of Intel EG20T"
depends on PCI
help
This driver is for PCH(Platform controller Hub) I2C of EG20T which
is an IOH(Input/Output Hub) for x86 embedded processor.
This driver can access PCH I2C bus device.
tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH"
depends on PCI
help
This driver is for PCH(Platform controller Hub) I2C of EG20T which
is an IOH(Input/Output Hub) for x86 embedded processor.
This driver can access PCH I2C bus device.
This driver also supports the ML7213, a companion chip for the
Atom E6xx series and compatible with the Intel EG20T PCH.
comment "External I2C/SMBus adapter drivers"
config I2C_DIOLAN_U2C
tristate "Diolan U2C-12 USB adapter"
depends on USB
help
If you say yes to this option, support will be included for Diolan
U2C-12, a USB to I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-diolan-u2c.
config I2C_PARPORT
tristate "Parallel port adapter"
depends on PARPORT

View File

@@ -54,6 +54,7 @@ obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
@@ -67,6 +68,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o

View File

@@ -0,0 +1,535 @@
/*
* Driver for the Diolan u2c-12 USB-I2C adapter
*
* Copyright (c) 2010-2011 Ericsson AB
*
* Derived from:
* i2c-tiny-usb.c
* Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org)
*
* 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, version 2.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#define DRIVER_NAME "i2c-diolan-u2c"
#define USB_VENDOR_ID_DIOLAN 0x0abf
#define USB_DEVICE_ID_DIOLAN_U2C 0x3370
#define DIOLAN_OUT_EP 0x02
#define DIOLAN_IN_EP 0x84
/* commands via USB, must match command ids in the firmware */
#define CMD_I2C_READ 0x01
#define CMD_I2C_WRITE 0x02
#define CMD_I2C_SCAN 0x03 /* Returns list of detected devices */
#define CMD_I2C_RELEASE_SDA 0x04
#define CMD_I2C_RELEASE_SCL 0x05
#define CMD_I2C_DROP_SDA 0x06
#define CMD_I2C_DROP_SCL 0x07
#define CMD_I2C_READ_SDA 0x08
#define CMD_I2C_READ_SCL 0x09
#define CMD_GET_FW_VERSION 0x0a
#define CMD_GET_SERIAL 0x0b
#define CMD_I2C_START 0x0c
#define CMD_I2C_STOP 0x0d
#define CMD_I2C_REPEATED_START 0x0e
#define CMD_I2C_PUT_BYTE 0x0f
#define CMD_I2C_GET_BYTE 0x10
#define CMD_I2C_PUT_ACK 0x11
#define CMD_I2C_GET_ACK 0x12
#define CMD_I2C_PUT_BYTE_ACK 0x13
#define CMD_I2C_GET_BYTE_ACK 0x14
#define CMD_I2C_SET_SPEED 0x1b
#define CMD_I2C_GET_SPEED 0x1c
#define CMD_I2C_SET_CLK_SYNC 0x24
#define CMD_I2C_GET_CLK_SYNC 0x25
#define CMD_I2C_SET_CLK_SYNC_TO 0x26
#define CMD_I2C_GET_CLK_SYNC_TO 0x27
#define RESP_OK 0x00
#define RESP_FAILED 0x01
#define RESP_BAD_MEMADDR 0x04
#define RESP_DATA_ERR 0x05
#define RESP_NOT_IMPLEMENTED 0x06
#define RESP_NACK 0x07
#define RESP_TIMEOUT 0x09
#define U2C_I2C_SPEED_FAST 0 /* 400 kHz */
#define U2C_I2C_SPEED_STD 1 /* 100 kHz */
#define U2C_I2C_SPEED_2KHZ 242 /* 2 kHz, minimum speed */
#define U2C_I2C_SPEED(f) ((DIV_ROUND_UP(1000000, (f)) - 10) / 2 + 1)
#define U2C_I2C_FREQ_FAST 400000
#define U2C_I2C_FREQ_STD 100000
#define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10))
#define DIOLAN_USB_TIMEOUT 100 /* in ms */
#define DIOLAN_SYNC_TIMEOUT 20 /* in ms */
#define DIOLAN_OUTBUF_LEN 128
#define DIOLAN_FLUSH_LEN (DIOLAN_OUTBUF_LEN - 4)
#define DIOLAN_INBUF_LEN 256 /* Maximum supported receive length */
/* Structure to hold all of our device specific stuff */
struct i2c_diolan_u2c {
u8 obuffer[DIOLAN_OUTBUF_LEN]; /* output buffer */
u8 ibuffer[DIOLAN_INBUF_LEN]; /* input buffer */
struct usb_device *usb_dev; /* the usb device for this device */
struct usb_interface *interface;/* the interface for this device */
struct i2c_adapter adapter; /* i2c related things */
int olen; /* Output buffer length */
int ocount; /* Number of enqueued messages */
};
static uint frequency = U2C_I2C_FREQ_STD; /* I2C clock frequency in Hz */
module_param(frequency, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(frequency, "I2C clock frequency in hertz");
/* usb layer */
/* Send command to device, and get response. */
static int diolan_usb_transfer(struct i2c_diolan_u2c *dev)
{
int ret = 0;
int actual;
int i;
if (!dev->olen || !dev->ocount)
return -EINVAL;
ret = usb_bulk_msg(dev->usb_dev,
usb_sndbulkpipe(dev->usb_dev, DIOLAN_OUT_EP),
dev->obuffer, dev->olen, &actual,
DIOLAN_USB_TIMEOUT);
if (!ret) {
for (i = 0; i < dev->ocount; i++) {
int tmpret;
tmpret = usb_bulk_msg(dev->usb_dev,
usb_rcvbulkpipe(dev->usb_dev,
DIOLAN_IN_EP),
dev->ibuffer,
sizeof(dev->ibuffer), &actual,
DIOLAN_USB_TIMEOUT);
/*
* Stop command processing if a previous command
* returned an error.
* Note that we still need to retrieve all messages.
*/
if (ret < 0)
continue;
ret = tmpret;
if (ret == 0 && actual > 0) {
switch (dev->ibuffer[actual - 1]) {
case RESP_NACK:
/*
* Return ENXIO if NACK was received as
* response to the address phase,
* EIO otherwise
*/
ret = i == 1 ? -ENXIO : -EIO;
break;
case RESP_TIMEOUT:
ret = -ETIMEDOUT;
break;
case RESP_OK:
/* strip off return code */
ret = actual - 1;
break;
default:
ret = -EIO;
break;
}
}
}
}
dev->olen = 0;
dev->ocount = 0;
return ret;
}
static int diolan_write_cmd(struct i2c_diolan_u2c *dev, bool flush)
{
if (flush || dev->olen >= DIOLAN_FLUSH_LEN)
return diolan_usb_transfer(dev);
return 0;
}
/* Send command (no data) */
static int diolan_usb_cmd(struct i2c_diolan_u2c *dev, u8 command, bool flush)
{
dev->obuffer[dev->olen++] = command;
dev->ocount++;
return diolan_write_cmd(dev, flush);
}
/* Send command with one byte of data */
static int diolan_usb_cmd_data(struct i2c_diolan_u2c *dev, u8 command, u8 data,
bool flush)
{
dev->obuffer[dev->olen++] = command;
dev->obuffer[dev->olen++] = data;
dev->ocount++;
return diolan_write_cmd(dev, flush);
}
/* Send command with two bytes of data */
static int diolan_usb_cmd_data2(struct i2c_diolan_u2c *dev, u8 command, u8 d1,
u8 d2, bool flush)
{
dev->obuffer[dev->olen++] = command;
dev->obuffer[dev->olen++] = d1;
dev->obuffer[dev->olen++] = d2;
dev->ocount++;
return diolan_write_cmd(dev, flush);
}
/*
* Flush input queue.
* If we don't do this at startup and the controller has queued up
* messages which were not retrieved, it will stop responding
* at some point.
*/
static void diolan_flush_input(struct i2c_diolan_u2c *dev)
{
int i;
for (i = 0; i < 10; i++) {
int actual = 0;
int ret;
ret = usb_bulk_msg(dev->usb_dev,
usb_rcvbulkpipe(dev->usb_dev, DIOLAN_IN_EP),
dev->ibuffer, sizeof(dev->ibuffer), &actual,
DIOLAN_USB_TIMEOUT);
if (ret < 0 || actual == 0)
break;
}
if (i == 10)
dev_err(&dev->interface->dev, "Failed to flush input buffer\n");
}
static int diolan_i2c_start(struct i2c_diolan_u2c *dev)
{
return diolan_usb_cmd(dev, CMD_I2C_START, false);
}
static int diolan_i2c_repeated_start(struct i2c_diolan_u2c *dev)
{
return diolan_usb_cmd(dev, CMD_I2C_REPEATED_START, false);
}
static int diolan_i2c_stop(struct i2c_diolan_u2c *dev)
{
return diolan_usb_cmd(dev, CMD_I2C_STOP, true);
}
static int diolan_i2c_get_byte_ack(struct i2c_diolan_u2c *dev, bool ack,
u8 *byte)
{
int ret;
ret = diolan_usb_cmd_data(dev, CMD_I2C_GET_BYTE_ACK, ack, true);
if (ret > 0)
*byte = dev->ibuffer[0];
else if (ret == 0)
ret = -EIO;
return ret;
}
static int diolan_i2c_put_byte_ack(struct i2c_diolan_u2c *dev, u8 byte)
{
return diolan_usb_cmd_data(dev, CMD_I2C_PUT_BYTE_ACK, byte, false);
}
static int diolan_set_speed(struct i2c_diolan_u2c *dev, u8 speed)
{
return diolan_usb_cmd_data(dev, CMD_I2C_SET_SPEED, speed, true);
}
/* Enable or disable clock synchronization (stretching) */
static int diolan_set_clock_synch(struct i2c_diolan_u2c *dev, bool enable)
{
return diolan_usb_cmd_data(dev, CMD_I2C_SET_CLK_SYNC, enable, true);
}
/* Set clock synchronization timeout in ms */
static int diolan_set_clock_synch_timeout(struct i2c_diolan_u2c *dev, int ms)
{
int to_val = ms * 10;
return diolan_usb_cmd_data2(dev, CMD_I2C_SET_CLK_SYNC_TO,
to_val & 0xff, (to_val >> 8) & 0xff, true);
}
static void diolan_fw_version(struct i2c_diolan_u2c *dev)
{
int ret;
ret = diolan_usb_cmd(dev, CMD_GET_FW_VERSION, true);
if (ret >= 2)
dev_info(&dev->interface->dev,
"Diolan U2C firmware version %u.%u\n",
(unsigned int)dev->ibuffer[0],
(unsigned int)dev->ibuffer[1]);
}
static void diolan_get_serial(struct i2c_diolan_u2c *dev)
{
int ret;
u32 serial;
ret = diolan_usb_cmd(dev, CMD_GET_SERIAL, true);
if (ret >= 4) {
serial = le32_to_cpu(*(u32 *)dev->ibuffer);
dev_info(&dev->interface->dev,
"Diolan U2C serial number %u\n", serial);
}
}
static int diolan_init(struct i2c_diolan_u2c *dev)
{
int speed, ret;
if (frequency >= 200000) {
speed = U2C_I2C_SPEED_FAST;
frequency = U2C_I2C_FREQ_FAST;
} else if (frequency >= 100000 || frequency == 0) {
speed = U2C_I2C_SPEED_STD;
frequency = U2C_I2C_FREQ_STD;
} else {
speed = U2C_I2C_SPEED(frequency);
if (speed > U2C_I2C_SPEED_2KHZ)
speed = U2C_I2C_SPEED_2KHZ;
frequency = U2C_I2C_FREQ(speed);
}
dev_info(&dev->interface->dev,
"Diolan U2C at USB bus %03d address %03d speed %d Hz\n",
dev->usb_dev->bus->busnum, dev->usb_dev->devnum, frequency);
diolan_flush_input(dev);
diolan_fw_version(dev);
diolan_get_serial(dev);
/* Set I2C speed */
ret = diolan_set_speed(dev, speed);
if (ret < 0)
return ret;
/* Configure I2C clock synchronization */
ret = diolan_set_clock_synch(dev, speed != U2C_I2C_SPEED_FAST);
if (ret < 0)
return ret;
if (speed != U2C_I2C_SPEED_FAST)
ret = diolan_set_clock_synch_timeout(dev, DIOLAN_SYNC_TIMEOUT);
return ret;
}
/* i2c layer */
static int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
int num)
{
struct i2c_diolan_u2c *dev = i2c_get_adapdata(adapter);
struct i2c_msg *pmsg;
int i, j;
int ret, sret;
ret = diolan_i2c_start(dev);
if (ret < 0)
return ret;
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
if (i) {
ret = diolan_i2c_repeated_start(dev);
if (ret < 0)
goto abort;
}
if (pmsg->flags & I2C_M_RD) {
ret =
diolan_i2c_put_byte_ack(dev, (pmsg->addr << 1) | 1);
if (ret < 0)
goto abort;
for (j = 0; j < pmsg->len; j++) {
u8 byte;
bool ack = j < pmsg->len - 1;
/*
* Don't send NACK if this is the first byte
* of a SMBUS_BLOCK message.
*/
if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN))
ack = true;
ret = diolan_i2c_get_byte_ack(dev, ack, &byte);
if (ret < 0)
goto abort;
/*
* Adjust count if first received byte is length
*/
if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) {
if (byte == 0
|| byte > I2C_SMBUS_BLOCK_MAX) {
ret = -EPROTO;
goto abort;
}
pmsg->len += byte;
}
pmsg->buf[j] = byte;
}
} else {
ret = diolan_i2c_put_byte_ack(dev, pmsg->addr << 1);
if (ret < 0)
goto abort;
for (j = 0; j < pmsg->len; j++) {
ret = diolan_i2c_put_byte_ack(dev,
pmsg->buf[j]);
if (ret < 0)
goto abort;
}
}
}
abort:
sret = diolan_i2c_stop(dev);
if (sret < 0 && ret >= 0)
ret = sret;
return ret;
}
/*
* Return list of supported functionality.
*/
static u32 diolan_usb_func(struct i2c_adapter *a)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
}
static const struct i2c_algorithm diolan_usb_algorithm = {
.master_xfer = diolan_usb_xfer,
.functionality = diolan_usb_func,
};
/* device layer */
static const struct usb_device_id diolan_u2c_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_DIOLAN, USB_DEVICE_ID_DIOLAN_U2C) },
{ }
};
MODULE_DEVICE_TABLE(usb, diolan_u2c_table);
static void diolan_u2c_free(struct i2c_diolan_u2c *dev)
{
usb_put_dev(dev->usb_dev);
kfree(dev);
}
static int diolan_u2c_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct i2c_diolan_u2c *dev;
int ret;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "no memory for device state\n");
ret = -ENOMEM;
goto error;
}
dev->usb_dev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* setup i2c adapter description */
dev->adapter.owner = THIS_MODULE;
dev->adapter.class = I2C_CLASS_HWMON;
dev->adapter.algo = &diolan_usb_algorithm;
i2c_set_adapdata(&dev->adapter, dev);
snprintf(dev->adapter.name, sizeof(dev->adapter.name),
DRIVER_NAME " at bus %03d device %03d",
dev->usb_dev->bus->busnum, dev->usb_dev->devnum);
dev->adapter.dev.parent = &dev->interface->dev;
/* initialize diolan i2c interface */
ret = diolan_init(dev);
if (ret < 0) {
dev_err(&interface->dev, "failed to initialize adapter\n");
goto error_free;
}
/* and finally attach to i2c layer */
ret = i2c_add_adapter(&dev->adapter);
if (ret < 0) {
dev_err(&interface->dev, "failed to add I2C adapter\n");
goto error_free;
}
dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n");
return 0;
error_free:
usb_set_intfdata(interface, NULL);
diolan_u2c_free(dev);
error:
return ret;
}
static void diolan_u2c_disconnect(struct usb_interface *interface)
{
struct i2c_diolan_u2c *dev = usb_get_intfdata(interface);
i2c_del_adapter(&dev->adapter);
usb_set_intfdata(interface, NULL);
diolan_u2c_free(dev);
dev_dbg(&interface->dev, "disconnected\n");
}
static struct usb_driver diolan_u2c_driver = {
.name = DRIVER_NAME,
.probe = diolan_u2c_probe,
.disconnect = diolan_u2c_disconnect,
.id_table = diolan_u2c_table,
};
static int __init diolan_u2c_init(void)
{
/* register this driver with the USB subsystem */
return usb_register(&diolan_u2c_driver);
}
static void __exit diolan_u2c_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&diolan_u2c_driver);
}
module_init(diolan_u2c_init);
module_exit(diolan_u2c_exit);
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
MODULE_DESCRIPTION(DRIVER_NAME " driver");
MODULE_LICENSE("GPL");

View File

@@ -132,6 +132,13 @@
#define pch_pci_dbg(pdev, fmt, arg...) \
dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg)
/*
Set the number of I2C instance max
Intel EG20T PCH : 1ch
OKI SEMICONDUCTOR ML7213 IOH : 2ch
*/
#define PCH_I2C_MAX_DEV 2
/**
* struct i2c_algo_pch_data - for I2C driver functionalities
* @pch_adapter: stores the reference to i2c_adapter structure
@@ -156,12 +163,14 @@ struct i2c_algo_pch_data {
* @pch_data: stores a list of i2c_algo_pch_data
* @pch_i2c_suspended: specifies whether the system is suspended or not
* perhaps with more lines and words.
* @ch_num: specifies the number of i2c instance
*
* pch_data has as many elements as maximum I2C channels
*/
struct adapter_info {
struct i2c_algo_pch_data pch_data;
struct i2c_algo_pch_data pch_data[PCH_I2C_MAX_DEV];
bool pch_i2c_suspended;
int ch_num;
};
@@ -170,8 +179,13 @@ static int pch_clk = 50000; /* specifies I2C clock speed in KHz */
static wait_queue_head_t pch_event;
static DEFINE_MUTEX(pch_mutex);
/* Definition for ML7213 by OKI SEMICONDUCTOR */
#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
static struct pci_device_id __devinitdata pch_pcidev_id[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_I2C)},
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, },
{0,}
};
@@ -212,8 +226,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap)
/* Initialize I2C registers */
iowrite32(0x21, p + PCH_I2CNF);
pch_setbit(adap->pch_base_address, PCH_I2CCTL,
PCH_I2CCTL_I2CMEN);
pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_I2CCTL_I2CMEN);
if (pch_i2c_speed != 400)
pch_i2c_speed = 100;
@@ -255,7 +268,7 @@ static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
* @timeout: waiting time counter (us).
*/
static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
s32 timeout)
s32 timeout)
{
void __iomem *p = adap->pch_base_address;
@@ -475,8 +488,8 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap)
* @last: specifies whether last message or not.
* @first: specifies whether first message or not.
*/
s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
u32 last, u32 first)
static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
u32 last, u32 first)
{
struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
@@ -569,10 +582,10 @@ s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
}
/**
* pch_i2c_cb_ch0() - Interrupt handler Call back function
* pch_i2c_cb() - Interrupt handler Call back function
* @adap: Pointer to struct i2c_algo_pch_data.
*/
static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap)
static void pch_i2c_cb(struct i2c_algo_pch_data *adap)
{
u32 sts;
void __iomem *p = adap->pch_base_address;
@@ -600,24 +613,30 @@ static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap)
*/
static irqreturn_t pch_i2c_handler(int irq, void *pData)
{
s32 reg_val;
u32 reg_val;
int flag;
int i;
struct adapter_info *adap_info = pData;
void __iomem *p;
u32 mode;
struct i2c_algo_pch_data *adap_data = (struct i2c_algo_pch_data *)pData;
void __iomem *p = adap_data->pch_base_address;
u32 mode = ioread32(p + PCH_I2CMOD) & (BUFFER_MODE | EEPROM_SR_MODE);
if (mode != NORMAL_MODE) {
pch_err(adap_data, "I2C mode is not supported\n");
return IRQ_NONE;
for (i = 0, flag = 0; i < adap_info->ch_num; i++) {
p = adap_info->pch_data[i].pch_base_address;
mode = ioread32(p + PCH_I2CMOD);
mode &= BUFFER_MODE | EEPROM_SR_MODE;
if (mode != NORMAL_MODE) {
pch_err(adap_info->pch_data,
"I2C-%d mode(%d) is not supported\n", mode, i);
continue;
}
reg_val = ioread32(p + PCH_I2CSR);
if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) {
pch_i2c_cb(&adap_info->pch_data[i]);
flag = 1;
}
}
reg_val = ioread32(p + PCH_I2CSR);
if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT))
pch_i2c_cb_ch0(adap_data);
else
return IRQ_NONE;
return IRQ_HANDLED;
return flag ? IRQ_HANDLED : IRQ_NONE;
}
/**
@@ -627,7 +646,7 @@ static irqreturn_t pch_i2c_handler(int irq, void *pData)
* @num: number of messages.
*/
static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, s32 num)
struct i2c_msg *msgs, s32 num)
{
struct i2c_msg *pmsg;
u32 i = 0;
@@ -710,11 +729,13 @@ static void pch_i2c_disbl_int(struct i2c_algo_pch_data *adap)
}
static int __devinit pch_i2c_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
const struct pci_device_id *id)
{
void __iomem *base_addr;
s32 ret;
int ret;
int i, j;
struct adapter_info *adap_info;
struct i2c_adapter *pch_adap;
pch_pci_dbg(pdev, "Entered.\n");
@@ -744,44 +765,48 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev,
goto err_pci_iomap;
}
adap_info->pch_i2c_suspended = false;
/* Set the number of I2C channel instance */
adap_info->ch_num = id->driver_data;
adap_info->pch_data.p_adapter_info = adap_info;
for (i = 0; i < adap_info->ch_num; i++) {
pch_adap = &adap_info->pch_data[i].pch_adapter;
adap_info->pch_i2c_suspended = false;
adap_info->pch_data.pch_adapter.owner = THIS_MODULE;
adap_info->pch_data.pch_adapter.class = I2C_CLASS_HWMON;
strcpy(adap_info->pch_data.pch_adapter.name, KBUILD_MODNAME);
adap_info->pch_data.pch_adapter.algo = &pch_algorithm;
adap_info->pch_data.pch_adapter.algo_data =
&adap_info->pch_data;
adap_info->pch_data[i].p_adapter_info = adap_info;
/* (i * 0x80) + base_addr; */
adap_info->pch_data.pch_base_address = base_addr;
pch_adap->owner = THIS_MODULE;
pch_adap->class = I2C_CLASS_HWMON;
strcpy(pch_adap->name, KBUILD_MODNAME);
pch_adap->algo = &pch_algorithm;
pch_adap->algo_data = &adap_info->pch_data[i];
adap_info->pch_data.pch_adapter.dev.parent = &pdev->dev;
/* base_addr + offset; */
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
ret = i2c_add_adapter(&(adap_info->pch_data.pch_adapter));
pch_adap->dev.parent = &pdev->dev;
if (ret) {
pch_pci_err(pdev, "i2c_add_adapter FAILED\n");
goto err_i2c_add_adapter;
ret = i2c_add_adapter(pch_adap);
if (ret) {
pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i);
goto err_i2c_add_adapter;
}
pch_i2c_init(&adap_info->pch_data[i]);
}
pch_i2c_init(&adap_info->pch_data);
ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED,
KBUILD_MODNAME, &adap_info->pch_data);
KBUILD_MODNAME, adap_info);
if (ret) {
pch_pci_err(pdev, "request_irq FAILED\n");
goto err_request_irq;
goto err_i2c_add_adapter;
}
pci_set_drvdata(pdev, adap_info);
pch_pci_dbg(pdev, "returns %d.\n", ret);
return 0;
err_request_irq:
i2c_del_adapter(&(adap_info->pch_data.pch_adapter));
err_i2c_add_adapter:
for (j = 0; j < i; j++)
i2c_del_adapter(&adap_info->pch_data[j].pch_adapter);
pci_iounmap(pdev, base_addr);
err_pci_iomap:
pci_release_regions(pdev);
@@ -794,17 +819,22 @@ err_pci_enable:
static void __devexit pch_i2c_remove(struct pci_dev *pdev)
{
int i;
struct adapter_info *adap_info = pci_get_drvdata(pdev);
pch_i2c_disbl_int(&adap_info->pch_data);
free_irq(pdev->irq, &adap_info->pch_data);
i2c_del_adapter(&(adap_info->pch_data.pch_adapter));
free_irq(pdev->irq, adap_info);
if (adap_info->pch_data.pch_base_address) {
pci_iounmap(pdev, adap_info->pch_data.pch_base_address);
adap_info->pch_data.pch_base_address = 0;
for (i = 0; i < adap_info->ch_num; i++) {
pch_i2c_disbl_int(&adap_info->pch_data[i]);
i2c_del_adapter(&adap_info->pch_data[i].pch_adapter);
}
if (adap_info->pch_data[0].pch_base_address)
pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address);
for (i = 0; i < adap_info->ch_num; i++)
adap_info->pch_data[i].pch_base_address = 0;
pci_set_drvdata(pdev, NULL);
pci_release_regions(pdev);
@@ -817,17 +847,22 @@ static void __devexit pch_i2c_remove(struct pci_dev *pdev)
static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state)
{
int ret;
int i;
struct adapter_info *adap_info = pci_get_drvdata(pdev);
void __iomem *p = adap_info->pch_data.pch_base_address;
void __iomem *p = adap_info->pch_data[0].pch_base_address;
adap_info->pch_i2c_suspended = true;
while ((adap_info->pch_data.pch_i2c_xfer_in_progress)) {
/* Wait until all channel transfers are completed */
msleep(20);
for (i = 0; i < adap_info->ch_num; i++) {
while ((adap_info->pch_data[i].pch_i2c_xfer_in_progress)) {
/* Wait until all channel transfers are completed */
msleep(20);
}
}
/* Disable the i2c interrupts */
pch_i2c_disbl_int(&adap_info->pch_data);
for (i = 0; i < adap_info->ch_num; i++)
pch_i2c_disbl_int(&adap_info->pch_data[i]);
pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x "
"invoked function pch_i2c_disbl_int successfully\n",
@@ -850,6 +885,7 @@ static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state)
static int pch_i2c_resume(struct pci_dev *pdev)
{
int i;
struct adapter_info *adap_info = pci_get_drvdata(pdev);
pci_set_power_state(pdev, PCI_D0);
@@ -862,7 +898,8 @@ static int pch_i2c_resume(struct pci_dev *pdev)
pci_enable_wake(pdev, PCI_D3hot, 0);
pch_i2c_init(&adap_info->pch_data);
for (i = 0; i < adap_info->ch_num; i++)
pch_i2c_init(&adap_info->pch_data[i]);
adap_info->pch_i2c_suspended = false;
@@ -894,7 +931,7 @@ static void __exit pch_pci_exit(void)
}
module_exit(pch_pci_exit);
MODULE_DESCRIPTION("PCH I2C PCI Driver");
MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH I2C Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.okisemi.com>");
module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR));

View File

@@ -118,6 +118,8 @@ static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
{
mxs_reset_block(i2c->regs);
writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
i2c->regs + MXS_I2C_QUEUECTRL_SET);
}
static void mxs_i2c_pioq_setup_read(struct mxs_i2c_dev *i2c, u8 addr, int len,
@@ -347,8 +349,6 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
/* Do reset to enforce correct startup after pinmuxing */
mxs_i2c_reset(i2c);
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
i2c->regs + MXS_I2C_QUEUECTRL_SET);
adap = &i2c->adapter;
strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name));

View File

@@ -0,0 +1,176 @@
/*
* The CE4100's I2C device is more or less the same one as found on PXA.
* It does not support slave mode, the register slightly moved. This PCI
* device provides three bars, every contains a single I2C controller.
*/
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/i2c/pxa-i2c.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#define CE4100_PCI_I2C_DEVS 3
struct ce4100_devices {
struct platform_device *pdev[CE4100_PCI_I2C_DEVS];
};
static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar)
{
struct platform_device *pdev;
struct i2c_pxa_platform_data pdata;
struct resource res[2];
struct device_node *child;
static int devnum;
int ret;
memset(&pdata, 0, sizeof(struct i2c_pxa_platform_data));
memset(&res, 0, sizeof(res));
res[0].flags = IORESOURCE_MEM;
res[0].start = pci_resource_start(dev, bar);
res[0].end = pci_resource_end(dev, bar);
res[1].flags = IORESOURCE_IRQ;
res[1].start = dev->irq;
res[1].end = dev->irq;
for_each_child_of_node(dev->dev.of_node, child) {
const void *prop;
struct resource r;
int ret;
ret = of_address_to_resource(child, 0, &r);
if (ret < 0)
continue;
if (r.start != res[0].start)
continue;
if (r.end != res[0].end)
continue;
if (r.flags != res[0].flags)
continue;
prop = of_get_property(child, "fast-mode", NULL);
if (prop)
pdata.fast_mode = 1;
break;
}
if (!child) {
dev_err(&dev->dev, "failed to match a DT node for bar %d.\n",
bar);
ret = -EINVAL;
goto out;
}
pdev = platform_device_alloc("ce4100-i2c", devnum);
if (!pdev) {
of_node_put(child);
ret = -ENOMEM;
goto out;
}
pdev->dev.parent = &dev->dev;
pdev->dev.of_node = child;
ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
if (ret)
goto err;
ret = platform_device_add_data(pdev, &pdata, sizeof(pdata));
if (ret)
goto err;
ret = platform_device_add(pdev);
if (ret)
goto err;
devnum++;
return pdev;
err:
platform_device_put(pdev);
out:
return ERR_PTR(ret);
}
static int __devinit ce4100_i2c_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
int ret;
int i;
struct ce4100_devices *sds;
ret = pci_enable_device_mem(dev);
if (ret)
return ret;
if (!dev->dev.of_node) {
dev_err(&dev->dev, "Missing device tree node.\n");
return -EINVAL;
}
sds = kzalloc(sizeof(*sds), GFP_KERNEL);
if (!sds)
goto err_mem;
for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) {
sds->pdev[i] = add_i2c_device(dev, i);
if (IS_ERR(sds->pdev[i])) {
while (--i >= 0)
platform_device_unregister(sds->pdev[i]);
goto err_dev_add;
}
}
pci_set_drvdata(dev, sds);
return 0;
err_dev_add:
pci_set_drvdata(dev, NULL);
kfree(sds);
err_mem:
pci_disable_device(dev);
return ret;
}
static void __devexit ce4100_i2c_remove(struct pci_dev *dev)
{
struct ce4100_devices *sds;
unsigned int i;
sds = pci_get_drvdata(dev);
pci_set_drvdata(dev, NULL);
for (i = 0; i < ARRAY_SIZE(sds->pdev); i++)
platform_device_unregister(sds->pdev[i]);
pci_disable_device(dev);
kfree(sds);
}
static struct pci_device_id ce4100_i2c_devices[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
{ },
};
MODULE_DEVICE_TABLE(pci, ce4100_i2c_devices);
static struct pci_driver ce4100_i2c_driver = {
.name = "ce4100_i2c",
.id_table = ce4100_i2c_devices,
.probe = ce4100_i2c_probe,
.remove = __devexit_p(ce4100_i2c_remove),
};
static int __init ce4100_i2c_init(void)
{
return pci_register_driver(&ce4100_i2c_driver);
}
module_init(ce4100_i2c_init);
static void __exit ce4100_i2c_exit(void)
{
pci_unregister_driver(&ce4100_i2c_driver);
}
module_exit(ce4100_i2c_exit);
MODULE_DESCRIPTION("CE4100 PCI-I2C glue code for PXA's driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");

View File

@@ -29,38 +29,75 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/i2c-pxa.h>
#include <linux/of_i2c.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/i2c/pxa-i2c.h>
#include <asm/irq.h>
#include <plat/i2c.h>
#ifndef CONFIG_HAVE_CLK
#define clk_get(dev, id) NULL
#define clk_put(clk) do { } while (0)
#define clk_disable(clk) do { } while (0)
#define clk_enable(clk) do { } while (0)
#endif
struct pxa_reg_layout {
u32 ibmr;
u32 idbr;
u32 icr;
u32 isr;
u32 isar;
};
enum pxa_i2c_types {
REGS_PXA2XX,
REGS_PXA3XX,
REGS_CE4100,
};
/*
* I2C register offsets will be shifted 0 or 1 bit left, depending on
* different SoCs
* I2C registers definitions
*/
#define REG_SHIFT_0 (0 << 0)
#define REG_SHIFT_1 (1 << 0)
#define REG_SHIFT(d) ((d) & 0x1)
static struct pxa_reg_layout pxa_reg_layout[] = {
[REGS_PXA2XX] = {
.ibmr = 0x00,
.idbr = 0x08,
.icr = 0x10,
.isr = 0x18,
.isar = 0x20,
},
[REGS_PXA3XX] = {
.ibmr = 0x00,
.idbr = 0x04,
.icr = 0x08,
.isr = 0x0c,
.isar = 0x10,
},
[REGS_CE4100] = {
.ibmr = 0x14,
.idbr = 0x0c,
.icr = 0x00,
.isr = 0x04,
/* no isar register */
},
};
static const struct platform_device_id i2c_pxa_id_table[] = {
{ "pxa2xx-i2c", REG_SHIFT_1 },
{ "pxa3xx-pwri2c", REG_SHIFT_0 },
{ "pxa2xx-i2c", REGS_PXA2XX },
{ "pxa3xx-pwri2c", REGS_PXA3XX },
{ "ce4100-i2c", REGS_CE4100 },
{ },
};
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
/*
* I2C registers and bit definitions
* I2C bit definitions
*/
#define IBMR (0x00)
#define IDBR (0x08)
#define ICR (0x10)
#define ISR (0x18)
#define ISAR (0x20)
#define ICR_START (1 << 0) /* start bit */
#define ICR_STOP (1 << 1) /* stop bit */
@@ -111,7 +148,11 @@ struct pxa_i2c {
u32 icrlog[32];
void __iomem *reg_base;
unsigned int reg_shift;
void __iomem *reg_ibmr;
void __iomem *reg_idbr;
void __iomem *reg_icr;
void __iomem *reg_isr;
void __iomem *reg_isar;
unsigned long iobase;
unsigned long iosize;
@@ -121,11 +162,11 @@ struct pxa_i2c {
unsigned int fast_mode :1;
};
#define _IBMR(i2c) ((i2c)->reg_base + (0x0 << (i2c)->reg_shift))
#define _IDBR(i2c) ((i2c)->reg_base + (0x4 << (i2c)->reg_shift))
#define _ICR(i2c) ((i2c)->reg_base + (0x8 << (i2c)->reg_shift))
#define _ISR(i2c) ((i2c)->reg_base + (0xc << (i2c)->reg_shift))
#define _ISAR(i2c) ((i2c)->reg_base + (0x10 << (i2c)->reg_shift))
#define _IBMR(i2c) ((i2c)->reg_ibmr)
#define _IDBR(i2c) ((i2c)->reg_idbr)
#define _ICR(i2c) ((i2c)->reg_icr)
#define _ISR(i2c) ((i2c)->reg_isr)
#define _ISAR(i2c) ((i2c)->reg_isar)
/*
* I2C Slave mode address
@@ -418,7 +459,8 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c)
writel(I2C_ISR_INIT, _ISR(i2c));
writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c));
writel(i2c->slave_addr, _ISAR(i2c));
if (i2c->reg_isar)
writel(i2c->slave_addr, _ISAR(i2c));
/* set control register values */
writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c));
@@ -729,8 +771,10 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num)
*/
ret = i2c->msg_idx;
if (timeout == 0)
if (!timeout && i2c->msg_num) {
i2c_pxa_scream_blue_murder(i2c, "timeout");
ret = I2C_RETRY;
}
out:
return ret;
@@ -915,11 +959,16 @@ static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr)
writel(icr, _ICR(i2c));
}
#define VALID_INT_SOURCE (ISR_SSD | ISR_ALD | ISR_ITE | ISR_IRF | \
ISR_SAD | ISR_BED)
static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id)
{
struct pxa_i2c *i2c = dev_id;
u32 isr = readl(_ISR(i2c));
if (!(isr & VALID_INT_SOURCE))
return IRQ_NONE;
if (i2c_debug > 2 && 0) {
dev_dbg(&i2c->adap.dev, "%s: ISR=%08x, ICR=%08x, IBMR=%02x\n",
__func__, isr, readl(_ICR(i2c)), readl(_IBMR(i2c)));
@@ -934,7 +983,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id)
/*
* Always clear all pending IRQs.
*/
writel(isr & (ISR_SSD|ISR_ALD|ISR_ITE|ISR_IRF|ISR_SAD|ISR_BED), _ISR(i2c));
writel(isr & VALID_INT_SOURCE, _ISR(i2c));
if (isr & ISR_SAD)
i2c_pxa_slave_start(i2c, isr);
@@ -1001,6 +1050,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
struct resource *res;
struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
const struct platform_device_id *id = platform_get_device_id(dev);
enum pxa_i2c_types i2c_type = id->driver_data;
int ret;
int irq;
@@ -1044,7 +1094,13 @@ static int i2c_pxa_probe(struct platform_device *dev)
ret = -EIO;
goto eremap;
}
i2c->reg_shift = REG_SHIFT(id->driver_data);
i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr;
i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr;
i2c->reg_icr = i2c->reg_base + pxa_reg_layout[i2c_type].icr;
i2c->reg_isr = i2c->reg_base + pxa_reg_layout[i2c_type].isr;
if (i2c_type != REGS_CE4100)
i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar;
i2c->iobase = res->start;
i2c->iosize = resource_size(res);
@@ -1072,7 +1128,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->adap.algo = &i2c_pxa_pio_algorithm;
} else {
i2c->adap.algo = &i2c_pxa_algorithm;
ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED,
ret = request_irq(irq, i2c_pxa_handler, IRQF_SHARED,
i2c->adap.name, i2c);
if (ret)
goto ereqirq;
@@ -1082,12 +1138,19 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &dev->dev;
#ifdef CONFIG_OF
i2c->adap.dev.of_node = dev->dev.of_node;
#endif
ret = i2c_add_numbered_adapter(&i2c->adap);
if (i2c_type == REGS_CE4100)
ret = i2c_add_adapter(&i2c->adap);
else
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
printk(KERN_INFO "I2C: Failed to add bus\n");
goto eadapt;
}
of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(dev, i2c);

View File

@@ -34,6 +34,16 @@ config LEDS_ATMEL_PWM
This option enables support for LEDs driven using outputs
of the dedicated PWM controller found on newer Atmel SOCs.
config LEDS_LM3530
tristate "LCD Backlight driver for LM3530"
depends on LEDS_CLASS
depends on I2C
help
This option enables support for the LCD backlight using
LM3530 ambient light sensor chip. This ALS chip can be
controlled manually or using PWM input or using ambient
light automatically.
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
depends on LEDS_CLASS

View File

@@ -9,6 +9,7 @@ obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o

View File

@@ -19,7 +19,7 @@
#include <linux/leds.h>
#include <linux/leds-bd2802.h>
#include <linux/slab.h>
#include <linux/pm.h>
#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
@@ -319,20 +319,6 @@ static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
bd2802_update_state(led, id, color, BD2802_OFF);
}
static void bd2802_restore_state(struct bd2802_led *led)
{
int i;
for (i = 0; i < LED_NUM; i++) {
if (led->led[i].r)
bd2802_turn_on(led, i, RED, led->led[i].r);
if (led->led[i].g)
bd2802_turn_on(led, i, GREEN, led->led[i].g);
if (led->led[i].b)
bd2802_turn_on(led, i, BLUE, led->led[i].b);
}
}
#define BD2802_SET_REGISTER(reg_addr, reg_name) \
static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
@@ -761,8 +747,25 @@ static int __exit bd2802_remove(struct i2c_client *client)
return 0;
}
static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
#ifdef CONFIG_PM
static void bd2802_restore_state(struct bd2802_led *led)
{
int i;
for (i = 0; i < LED_NUM; i++) {
if (led->led[i].r)
bd2802_turn_on(led, i, RED, led->led[i].r);
if (led->led[i].g)
bd2802_turn_on(led, i, GREEN, led->led[i].g);
if (led->led[i].b)
bd2802_turn_on(led, i, BLUE, led->led[i].b);
}
}
static int bd2802_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bd2802_led *led = i2c_get_clientdata(client);
gpio_set_value(led->pdata->reset_gpio, 0);
@@ -770,8 +773,9 @@ static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
static int bd2802_resume(struct i2c_client *client)
static int bd2802_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bd2802_led *led = i2c_get_clientdata(client);
if (!bd2802_is_all_off(led) || led->adf_on) {
@@ -782,6 +786,12 @@ static int bd2802_resume(struct i2c_client *client)
return 0;
}
static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
#define BD2802_PM (&bd2802_pm)
#else /* CONFIG_PM */
#define BD2802_PM NULL
#endif
static const struct i2c_device_id bd2802_id[] = {
{ "BD2802", 0 },
{ }
@@ -791,11 +801,10 @@ MODULE_DEVICE_TABLE(i2c, bd2802_id);
static struct i2c_driver bd2802_i2c_driver = {
.driver = {
.name = "BD2802",
.pm = BD2802_PM,
},
.probe = bd2802_probe,
.remove = __exit_p(bd2802_remove),
.suspend = bd2802_suspend,
.resume = bd2802_resume,
.id_table = bd2802_id,
};

378
drivers/leds/leds-lm3530.c Normal file
View File

@@ -0,0 +1,378 @@
/*
* Copyright (C) 2011 ST-Ericsson SA.
* Copyright (C) 2009 Motorola, Inc.
*
* License Terms: GNU General Public License v2
*
* Simple driver for National Semiconductor LM3530 Backlight driver chip
*
* Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
* based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>
*/
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/led-lm3530.h>
#include <linux/types.h>
#define LM3530_LED_DEV "lcd-backlight"
#define LM3530_NAME "lm3530-led"
#define LM3530_GEN_CONFIG 0x10
#define LM3530_ALS_CONFIG 0x20
#define LM3530_BRT_RAMP_RATE 0x30
#define LM3530_ALS_ZONE_REG 0x40
#define LM3530_ALS_IMP_SELECT 0x41
#define LM3530_BRT_CTRL_REG 0xA0
#define LM3530_ALS_ZB0_REG 0x60
#define LM3530_ALS_ZB1_REG 0x61
#define LM3530_ALS_ZB2_REG 0x62
#define LM3530_ALS_ZB3_REG 0x63
#define LM3530_ALS_Z0T_REG 0x70
#define LM3530_ALS_Z1T_REG 0x71
#define LM3530_ALS_Z2T_REG 0x72
#define LM3530_ALS_Z3T_REG 0x73
#define LM3530_ALS_Z4T_REG 0x74
#define LM3530_REG_MAX 15
/* General Control Register */
#define LM3530_EN_I2C_SHIFT (0)
#define LM3530_RAMP_LAW_SHIFT (1)
#define LM3530_MAX_CURR_SHIFT (2)
#define LM3530_EN_PWM_SHIFT (5)
#define LM3530_PWM_POL_SHIFT (6)
#define LM3530_EN_PWM_SIMPLE_SHIFT (7)
#define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT)
#define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT)
#define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT)
#define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT)
/* ALS Config Register Options */
#define LM3530_ALS_AVG_TIME_SHIFT (0)
#define LM3530_EN_ALS_SHIFT (3)
#define LM3530_ALS_SEL_SHIFT (5)
#define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT)
/* Brightness Ramp Rate Register */
#define LM3530_BRT_RAMP_FALL_SHIFT (0)
#define LM3530_BRT_RAMP_RISE_SHIFT (3)
/* ALS Resistor Select */
#define LM3530_ALS1_IMP_SHIFT (0)
#define LM3530_ALS2_IMP_SHIFT (4)
/* Zone Boundary Register defaults */
#define LM3530_DEF_ZB_0 (0x33)
#define LM3530_DEF_ZB_1 (0x66)
#define LM3530_DEF_ZB_2 (0x99)
#define LM3530_DEF_ZB_3 (0xCC)
/* Zone Target Register defaults */
#define LM3530_DEF_ZT_0 (0x19)
#define LM3530_DEF_ZT_1 (0x33)
#define LM3530_DEF_ZT_2 (0x4C)
#define LM3530_DEF_ZT_3 (0x66)
#define LM3530_DEF_ZT_4 (0x7F)
struct lm3530_mode_map {
const char *mode;
enum lm3530_mode mode_val;
};
static struct lm3530_mode_map mode_map[] = {
{ "man", LM3530_BL_MODE_MANUAL },
{ "als", LM3530_BL_MODE_ALS },
{ "pwm", LM3530_BL_MODE_PWM },
};
/**
* struct lm3530_data
* @led_dev: led class device
* @client: i2c client
* @pdata: LM3530 platform data
* @mode: mode of operation - manual, ALS, PWM
*/
struct lm3530_data {
struct led_classdev led_dev;
struct i2c_client *client;
struct lm3530_platform_data *pdata;
enum lm3530_mode mode;
};
static const u8 lm3530_reg[LM3530_REG_MAX] = {
LM3530_GEN_CONFIG,
LM3530_ALS_CONFIG,
LM3530_BRT_RAMP_RATE,
LM3530_ALS_ZONE_REG,
LM3530_ALS_IMP_SELECT,
LM3530_BRT_CTRL_REG,
LM3530_ALS_ZB0_REG,
LM3530_ALS_ZB1_REG,
LM3530_ALS_ZB2_REG,
LM3530_ALS_ZB3_REG,
LM3530_ALS_Z0T_REG,
LM3530_ALS_Z1T_REG,
LM3530_ALS_Z2T_REG,
LM3530_ALS_Z3T_REG,
LM3530_ALS_Z4T_REG,
};
static int lm3530_get_mode_from_str(const char *str)
{
int i;
for (i = 0; i < ARRAY_SIZE(mode_map); i++)
if (sysfs_streq(str, mode_map[i].mode))
return mode_map[i].mode_val;
return -1;
}
static int lm3530_init_registers(struct lm3530_data *drvdata)
{
int ret = 0;
int i;
u8 gen_config;
u8 als_config = 0;
u8 brt_ramp;
u8 als_imp_sel = 0;
u8 brightness;
u8 reg_val[LM3530_REG_MAX];
struct lm3530_platform_data *pltfm = drvdata->pdata;
struct i2c_client *client = drvdata->client;
gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT);
if (drvdata->mode == LM3530_BL_MODE_MANUAL ||
drvdata->mode == LM3530_BL_MODE_ALS)
gen_config |= (LM3530_ENABLE_I2C);
if (drvdata->mode == LM3530_BL_MODE_ALS) {
als_config =
(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
(LM3530_ENABLE_ALS) |
(pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT);
als_imp_sel =
(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
}
if (drvdata->mode == LM3530_BL_MODE_PWM)
gen_config |= (LM3530_ENABLE_PWM) |
(pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) |
(LM3530_ENABLE_PWM_SIMPLE);
brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
(pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
brightness = pltfm->brt_val;
reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */
reg_val[1] = als_config; /* LM3530_ALS_CONFIG */
reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */
reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */
reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */
reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */
reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */
reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
for (i = 0; i < LM3530_REG_MAX; i++) {
ret = i2c_smbus_write_byte_data(client,
lm3530_reg[i], reg_val[i]);
if (ret)
break;
}
return ret;
}
static void lm3530_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brt_val)
{
int err;
struct lm3530_data *drvdata =
container_of(led_cdev, struct lm3530_data, led_dev);
switch (drvdata->mode) {
case LM3530_BL_MODE_MANUAL:
/* set the brightness in brightness control register*/
err = i2c_smbus_write_byte_data(drvdata->client,
LM3530_BRT_CTRL_REG, brt_val / 2);
if (err)
dev_err(&drvdata->client->dev,
"Unable to set brightness: %d\n", err);
break;
case LM3530_BL_MODE_ALS:
break;
case LM3530_BL_MODE_PWM:
break;
default:
break;
}
}
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
int err;
struct i2c_client *client = container_of(
dev->parent, struct i2c_client, dev);
struct lm3530_data *drvdata = i2c_get_clientdata(client);
int mode;
mode = lm3530_get_mode_from_str(buf);
if (mode < 0) {
dev_err(dev, "Invalid mode\n");
return -EINVAL;
}
if (mode == LM3530_BL_MODE_MANUAL)
drvdata->mode = LM3530_BL_MODE_MANUAL;
else if (mode == LM3530_BL_MODE_ALS)
drvdata->mode = LM3530_BL_MODE_ALS;
else if (mode == LM3530_BL_MODE_PWM) {
dev_err(dev, "PWM mode not supported\n");
return -EINVAL;
}
err = lm3530_init_registers(drvdata);
if (err) {
dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
return err;
}
return sizeof(drvdata->mode);
}
static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
static int __devinit lm3530_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lm3530_platform_data *pdata = client->dev.platform_data;
struct lm3530_data *drvdata;
int err = 0;
if (pdata == NULL) {
dev_err(&client->dev, "platform data required\n");
err = -ENODEV;
goto err_out;
}
/* BL mode */
if (pdata->mode > LM3530_BL_MODE_PWM) {
dev_err(&client->dev, "Illegal Mode request\n");
err = -EINVAL;
goto err_out;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
err = -EIO;
goto err_out;
}
drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL);
if (drvdata == NULL) {
err = -ENOMEM;
goto err_out;
}
drvdata->mode = pdata->mode;
drvdata->client = client;
drvdata->pdata = pdata;
drvdata->led_dev.name = LM3530_LED_DEV;
drvdata->led_dev.brightness_set = lm3530_brightness_set;
i2c_set_clientdata(client, drvdata);
err = lm3530_init_registers(drvdata);
if (err < 0) {
dev_err(&client->dev, "Register Init failed: %d\n", err);
err = -ENODEV;
goto err_reg_init;
}
err = led_classdev_register((struct device *)
&client->dev, &drvdata->led_dev);
if (err < 0) {
dev_err(&client->dev, "Register led class failed: %d\n", err);
err = -ENODEV;
goto err_class_register;
}
err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode);
if (err < 0) {
dev_err(&client->dev, "File device creation failed: %d\n", err);
err = -ENODEV;
goto err_create_file;
}
return 0;
err_create_file:
led_classdev_unregister(&drvdata->led_dev);
err_class_register:
err_reg_init:
kfree(drvdata);
err_out:
return err;
}
static int __devexit lm3530_remove(struct i2c_client *client)
{
struct lm3530_data *drvdata = i2c_get_clientdata(client);
device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
led_classdev_unregister(&drvdata->led_dev);
kfree(drvdata);
return 0;
}
static const struct i2c_device_id lm3530_id[] = {
{LM3530_NAME, 0},
{}
};
static struct i2c_driver lm3530_i2c_driver = {
.probe = lm3530_probe,
.remove = lm3530_remove,
.id_table = lm3530_id,
.driver = {
.name = LM3530_NAME,
.owner = THIS_MODULE,
},
};
static int __init lm3530_init(void)
{
return i2c_add_driver(&lm3530_i2c_driver);
}
static void __exit lm3530_exit(void)
{
i2c_del_driver(&lm3530_i2c_driver);
}
module_init(lm3530_init);
module_exit(lm3530_exit);
MODULE_DESCRIPTION("Back Light driver for LM3530");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");

View File

@@ -534,7 +534,7 @@ static ssize_t lp5521_selftest(struct device *dev,
}
/* led class device attributes */
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current);
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current);
static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL);
static struct attribute *lp5521_led_attributes[] = {
@@ -548,15 +548,15 @@ static struct attribute_group lp5521_led_attribute_group = {
};
/* device attributes */
static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR,
show_engine1_mode, store_engine1_mode);
static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR,
show_engine2_mode, store_engine2_mode);
static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR,
show_engine3_mode, store_engine3_mode);
static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load);
static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load);
static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load);
static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load);
static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load);
static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load);
static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
static struct attribute *lp5521_attributes[] = {

View File

@@ -713,7 +713,7 @@ static ssize_t store_current(struct device *dev,
}
/* led class device attributes */
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current);
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current);
static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL);
static struct attribute *lp5523_led_attributes[] = {
@@ -727,21 +727,21 @@ static struct attribute_group lp5523_led_attribute_group = {
};
/* device attributes */
static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR,
show_engine1_mode, store_engine1_mode);
static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR,
show_engine2_mode, store_engine2_mode);
static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR,
show_engine3_mode, store_engine3_mode);
static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR,
show_engine1_leds, store_engine1_leds);
static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR,
show_engine2_leds, store_engine2_leds);
static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO,
static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR,
show_engine3_leds, store_engine3_leds);
static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load);
static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load);
static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load);
static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load);
static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load);
static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load);
static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL);
static struct attribute *lp5523_attributes[] = {

View File

@@ -19,7 +19,7 @@
#include <asm/geode.h>
static struct gpio_led net5501_leds[] = {
static const struct gpio_led net5501_leds[] = {
{
.name = "error",
.gpio = 6,

View File

@@ -163,6 +163,7 @@ void __init pmu_backlight_init()
snprintf(name, sizeof(name), "pmubl");
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,
&props);

View File

@@ -402,6 +402,16 @@ config DS1682
This driver can also be built as a module. If so, the module
will be called ds1682.
config SPEAR13XX_PCIE_GADGET
bool "PCIe gadget support for SPEAr13XX platform"
depends on ARCH_SPEAR13XX
default n
help
This option enables gadget support for PCIe controller. If
board file defines any controller as PCIe endpoint then a sysfs
entry will be created for that controller. User can use these
sysfs node to configure PCIe EP as per his requirements.
config TI_DAC7512
tristate "Texas Instruments DAC7512"
depends on SPI && SYSFS

View File

@@ -37,6 +37,7 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
obj-y += cb710/
obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o
obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_PCH_PHUB) += pch_phub.o

View File

@@ -245,9 +245,8 @@ static int apds9802als_probe(struct i2c_client *client,
als_set_default_config(client);
mutex_init(&data->mutex);
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
pm_runtime_get(&client->dev);
pm_runtime_put(&client->dev);
return res;
als_error1:
@@ -255,12 +254,19 @@ als_error1:
return res;
}
static int apds9802als_remove(struct i2c_client *client)
static int __devexit apds9802als_remove(struct i2c_client *client)
{
struct als_data *data = i2c_get_clientdata(client);
pm_runtime_get_sync(&client->dev);
als_set_power_state(client, false);
sysfs_remove_group(&client->dev.kobj, &m_als_gr);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
kfree(data);
return 0;
}
@@ -275,9 +281,6 @@ static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg)
static int apds9802als_resume(struct i2c_client *client)
{
als_set_default_config(client);
pm_runtime_get(&client->dev);
pm_runtime_put(&client->dev);
return 0;
}
@@ -323,7 +326,7 @@ static struct i2c_driver apds9802als_driver = {
.pm = APDS9802ALS_PM_OPS,
},
.probe = apds9802als_probe,
.remove = apds9802als_remove,
.remove = __devexit_p(apds9802als_remove),
.suspend = apds9802als_suspend,
.resume = apds9802als_resume,
.id_table = apds9802als_id,

View File

@@ -75,7 +75,7 @@ out:
return tc;
fail_ioremap:
release_resource(r);
release_mem_region(r->start, ATMEL_TC_IOMEM_SIZE);
fail:
tc = NULL;
goto out;
@@ -95,7 +95,7 @@ void atmel_tc_free(struct atmel_tc *tc)
spin_lock(&tc_list_lock);
if (tc->regs) {
iounmap(tc->regs);
release_resource(tc->iomem);
release_mem_region(tc->iomem->start, ATMEL_TC_IOMEM_SIZE);
tc->regs = NULL;
tc->iomem = NULL;
}

View File

@@ -196,10 +196,11 @@ static int __devexit bh1780_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
static int bh1780_suspend(struct i2c_client *client, pm_message_t mesg)
static int bh1780_suspend(struct device *dev)
{
struct bh1780_data *ddata;
int state, ret;
struct i2c_client *client = to_i2c_client(dev);
ddata = i2c_get_clientdata(client);
state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL");
@@ -217,14 +218,14 @@ static int bh1780_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
static int bh1780_resume(struct i2c_client *client)
static int bh1780_resume(struct device *dev)
{
struct bh1780_data *ddata;
int state, ret;
struct i2c_client *client = to_i2c_client(dev);
ddata = i2c_get_clientdata(client);
state = ddata->power_state;
ret = bh1780_write(ddata, BH1780_REG_CONTROL, state,
"CONTROL");
@@ -233,9 +234,10 @@ static int bh1780_resume(struct i2c_client *client)
return 0;
}
static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume);
#define BH1780_PMOPS (&bh1780_pm)
#else
#define bh1780_suspend NULL
#define bh1780_resume NULL
#define BH1780_PMOPS NULL
#endif /* CONFIG_PM */
static const struct i2c_device_id bh1780_id[] = {
@@ -247,11 +249,10 @@ static struct i2c_driver bh1780_driver = {
.probe = bh1780_probe,
.remove = bh1780_remove,
.id_table = bh1780_id,
.suspend = bh1780_suspend,
.resume = bh1780_resume,
.driver = {
.name = "bh1780"
},
.name = "bh1780",
.pm = BH1780_PMOPS,
},
};
static int __init bh1780_init(void)

View File

@@ -402,7 +402,7 @@ exit:
return status;
}
static int bmp085_probe(struct i2c_client *client,
static int __devinit bmp085_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bmp085_data *data;
@@ -438,7 +438,7 @@ exit:
return err;
}
static int bmp085_remove(struct i2c_client *client)
static int __devexit bmp085_remove(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &bmp085_attr_group);
kfree(i2c_get_clientdata(client));
@@ -458,7 +458,7 @@ static struct i2c_driver bmp085_driver = {
},
.id_table = bmp085_id,
.probe = bmp085_probe,
.remove = bmp085_remove,
.remove = __devexit_p(bmp085_remove),
.detect = bmp085_detect,
.address_list = normal_i2c

View File

@@ -249,11 +249,11 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev,
static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL);
static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL);
static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO,
static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO,
ep93xx_pwm_get_freq, ep93xx_pwm_set_freq);
static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO,
static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO,
ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent);
static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO,
static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO,
ep93xx_pwm_get_invert, ep93xx_pwm_set_invert);
static struct attribute *ep93xx_pwm_attrs[] = {

View File

@@ -75,7 +75,7 @@ static ssize_t compass_heading_data_show(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
unsigned char i2c_data[2];
unsigned int ret;
int ret;
mutex_lock(&compass_mutex);
ret = compass_command(client, 'A');
@@ -86,7 +86,7 @@ static ssize_t compass_heading_data_show(struct device *dev,
msleep(10); /* sending 'A' cmd we need to wait for 7-10 millisecs */
ret = i2c_master_recv(client, i2c_data, 2);
mutex_unlock(&compass_mutex);
if (ret != 1) {
if (ret < 0) {
dev_warn(dev, "i2c read data cmd failed\n");
return ret;
}

View File

@@ -735,6 +735,7 @@ static struct pci_device_id pch_phub_pcidev_id[] = {
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), 2, },
{ }
};
MODULE_DEVICE_TABLE(pci, pch_phub_pcidev_id);
static struct pci_driver pch_phub_driver = {
.name = "pch_phub",

View File

@@ -0,0 +1,908 @@
/*
* drivers/misc/spear13xx_pcie_gadget.c
*
* Copyright (C) 2010 ST Microelectronics
* Pratyush Anand<pratyush.anand@st.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pci_regs.h>
#include <linux/configfs.h>
#include <mach/pcie.h>
#include <mach/misc_regs.h>
#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
/* In current implementation address translation is done using IN0 only.
* So IN1 start address and IN0 end address has been kept same
*/
#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1)
#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1)
#define IN_MSG_SIZE (12 * 1024 * 1024 - 1)
/* Keep default BAR size as 4K*/
/* AORAM would be mapped by default*/
#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1)
#define INT_TYPE_NO_INT 0
#define INT_TYPE_INTX 1
#define INT_TYPE_MSI 2
struct spear_pcie_gadget_config {
void __iomem *base;
void __iomem *va_app_base;
void __iomem *va_dbi_base;
char int_type[10];
ulong requested_msi;
ulong configured_msi;
ulong bar0_size;
ulong bar0_rw_offset;
void __iomem *va_bar0_address;
};
struct pcie_gadget_target {
struct configfs_subsystem subsys;
struct spear_pcie_gadget_config config;
};
struct pcie_gadget_target_attr {
struct configfs_attribute attr;
ssize_t (*show)(struct spear_pcie_gadget_config *config,
char *buf);
ssize_t (*store)(struct spear_pcie_gadget_config *config,
const char *buf,
size_t count);
};
static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
{
/* Enable DBI access */
writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_armisc);
writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_awmisc);
}
static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
{
/* disable DBI access */
writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_armisc);
writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_awmisc);
}
static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
int where, int size, u32 *val)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong va_address;
/* Enable DBI access */
enable_dbi_access(app_reg);
va_address = (ulong)config->va_dbi_base + (where & ~0x3);
*val = readl(va_address);
if (size == 1)
*val = (*val >> (8 * (where & 3))) & 0xff;
else if (size == 2)
*val = (*val >> (8 * (where & 3))) & 0xffff;
/* Disable DBI access */
disable_dbi_access(app_reg);
}
static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
int where, int size, u32 val)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong va_address;
/* Enable DBI access */
enable_dbi_access(app_reg);
va_address = (ulong)config->va_dbi_base + (where & ~0x3);
if (size == 4)
writel(val, va_address);
else if (size == 2)
writew(val, va_address + (where & 2));
else if (size == 1)
writeb(val, va_address + (where & 3));
/* Disable DBI access */
disable_dbi_access(app_reg);
}
#define PCI_FIND_CAP_TTL 48
static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config,
u32 pos, int cap, int *ttl)
{
u32 id;
while ((*ttl)--) {
spear_dbi_read_reg(config, pos, 1, &pos);
if (pos < 0x40)
break;
pos &= ~3;
spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id);
if (id == 0xff)
break;
if (id == cap)
return pos;
pos += PCI_CAP_LIST_NEXT;
}
return 0;
}
static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config,
u32 pos, int cap)
{
int ttl = PCI_FIND_CAP_TTL;
return pci_find_own_next_cap_ttl(config, pos, cap, &ttl);
}
static int pci_find_own_cap_start(struct spear_pcie_gadget_config *config,
u8 hdr_type)
{
u32 status;
spear_dbi_read_reg(config, PCI_STATUS, 2, &status);
if (!(status & PCI_STATUS_CAP_LIST))
return 0;
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
return PCI_CAPABILITY_LIST;
case PCI_HEADER_TYPE_CARDBUS:
return PCI_CB_CAPABILITY_LIST;
default:
return 0;
}
return 0;
}
/*
* Tell if a device supports a given PCI capability.
* Returns the address of the requested capability structure within the
* device's PCI configuration space or 0 in case the device does not
* support it. Possible values for @cap:
*
* %PCI_CAP_ID_PM Power Management
* %PCI_CAP_ID_AGP Accelerated Graphics Port
* %PCI_CAP_ID_VPD Vital Product Data
* %PCI_CAP_ID_SLOTID Slot Identification
* %PCI_CAP_ID_MSI Message Signalled Interrupts
* %PCI_CAP_ID_CHSWP CompactPCI HotSwap
* %PCI_CAP_ID_PCIX PCI-X
* %PCI_CAP_ID_EXP PCI Express
*/
static int pci_find_own_capability(struct spear_pcie_gadget_config *config,
int cap)
{
u32 pos;
u32 hdr_type;
spear_dbi_read_reg(config, PCI_HEADER_TYPE, 1, &hdr_type);
pos = pci_find_own_cap_start(config, hdr_type);
if (pos)
pos = pci_find_own_next_cap(config, pos, cap);
return pos;
}
static irqreturn_t spear_pcie_gadget_irq(int irq, void *dev_id)
{
return 0;
}
/*
* configfs interfaces show/store functions
*/
static ssize_t pcie_gadget_show_link(
struct spear_pcie_gadget_config *config,
char *buf)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID))
return sprintf(buf, "UP");
else
return sprintf(buf, "DOWN");
}
static ssize_t pcie_gadget_store_link(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
if (sysfs_streq(buf, "UP"))
writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID),
&app_reg->app_ctrl_0);
else if (sysfs_streq(buf, "DOWN"))
writel(readl(&app_reg->app_ctrl_0)
& ~(1 << APP_LTSSM_ENABLE_ID),
&app_reg->app_ctrl_0);
else
return -EINVAL;
return count;
}
static ssize_t pcie_gadget_show_int_type(
struct spear_pcie_gadget_config *config,
char *buf)
{
return sprintf(buf, "%s", config->int_type);
}
static ssize_t pcie_gadget_store_int_type(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
u32 cap, vec, flags;
ulong vector;
if (sysfs_streq(buf, "INTA"))
spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
else if (sysfs_streq(buf, "MSI")) {
vector = config->requested_msi;
vec = 0;
while (vector > 1) {
vector /= 2;
vec++;
}
spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0);
cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
flags &= ~PCI_MSI_FLAGS_QMASK;
flags |= vec << 1;
spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags);
} else
return -EINVAL;
strcpy(config->int_type, buf);
return count;
}
static ssize_t pcie_gadget_show_no_of_msi(
struct spear_pcie_gadget_config *config,
char *buf)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
u32 cap, vec, flags;
ulong vector;
if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID))
!= (1 << CFG_MSI_EN_ID))
vector = 0;
else {
cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
flags &= ~PCI_MSI_FLAGS_QSIZE;
vec = flags >> 4;
vector = 1;
while (vec--)
vector *= 2;
}
config->configured_msi = vector;
return sprintf(buf, "%lu", vector);
}
static ssize_t pcie_gadget_store_no_of_msi(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
if (strict_strtoul(buf, 0, &config->requested_msi))
return -EINVAL;
if (config->requested_msi > 32)
config->requested_msi = 32;
return count;
}
static ssize_t pcie_gadget_store_inta(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong en;
if (strict_strtoul(buf, 0, &en))
return -EINVAL;
if (en)
writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
&app_reg->app_ctrl_0);
else
writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID),
&app_reg->app_ctrl_0);
return count;
}
static ssize_t pcie_gadget_store_send_msi(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong vector;
u32 ven_msi;
if (strict_strtoul(buf, 0, &vector))
return -EINVAL;
if (!config->configured_msi)
return -EINVAL;
if (vector >= config->configured_msi)
return -EINVAL;
ven_msi = readl(&app_reg->ven_msi_1);
ven_msi &= ~VEN_MSI_FUN_NUM_MASK;
ven_msi |= 0 << VEN_MSI_FUN_NUM_ID;
ven_msi &= ~VEN_MSI_TC_MASK;
ven_msi |= 0 << VEN_MSI_TC_ID;
ven_msi &= ~VEN_MSI_VECTOR_MASK;
ven_msi |= vector << VEN_MSI_VECTOR_ID;
/* generating interrupt for msi vector */
ven_msi |= VEN_MSI_REQ_EN;
writel(ven_msi, &app_reg->ven_msi_1);
udelay(1);
ven_msi &= ~VEN_MSI_REQ_EN;
writel(ven_msi, &app_reg->ven_msi_1);
return count;
}
static ssize_t pcie_gadget_show_vendor_id(
struct spear_pcie_gadget_config *config,
char *buf)
{
u32 id;
spear_dbi_read_reg(config, PCI_VENDOR_ID, 2, &id);
return sprintf(buf, "%x", id);
}
static ssize_t pcie_gadget_store_vendor_id(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
ulong id;
if (strict_strtoul(buf, 0, &id))
return -EINVAL;
spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
return count;
}
static ssize_t pcie_gadget_show_device_id(
struct spear_pcie_gadget_config *config,
char *buf)
{
u32 id;
spear_dbi_read_reg(config, PCI_DEVICE_ID, 2, &id);
return sprintf(buf, "%x", id);
}
static ssize_t pcie_gadget_store_device_id(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
ulong id;
if (strict_strtoul(buf, 0, &id))
return -EINVAL;
spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
return count;
}
static ssize_t pcie_gadget_show_bar0_size(
struct spear_pcie_gadget_config *config,
char *buf)
{
return sprintf(buf, "%lx", config->bar0_size);
}
static ssize_t pcie_gadget_store_bar0_size(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
ulong size;
u32 pos, pos1;
u32 no_of_bit = 0;
if (strict_strtoul(buf, 0, &size))
return -EINVAL;
/* min bar size is 256 */
if (size <= 0x100)
size = 0x100;
/* max bar size is 1MB*/
else if (size >= 0x100000)
size = 0x100000;
else {
pos = 0;
pos1 = 0;
while (pos < 21) {
pos = find_next_bit((ulong *)&size, 21, pos);
if (pos != 21)
pos1 = pos + 1;
pos++;
no_of_bit++;
}
if (no_of_bit == 2)
pos1--;
size = 1 << pos1;
}
config->bar0_size = size;
spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1);
return count;
}
static ssize_t pcie_gadget_show_bar0_address(
struct spear_pcie_gadget_config *config,
char *buf)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
u32 address = readl(&app_reg->pim0_mem_addr_start);
return sprintf(buf, "%x", address);
}
static ssize_t pcie_gadget_store_bar0_address(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong address;
if (strict_strtoul(buf, 0, &address))
return -EINVAL;
address &= ~(config->bar0_size - 1);
if (config->va_bar0_address)
iounmap(config->va_bar0_address);
config->va_bar0_address = ioremap(address, config->bar0_size);
if (!config->va_bar0_address)
return -ENOMEM;
writel(address, &app_reg->pim0_mem_addr_start);
return count;
}
static ssize_t pcie_gadget_show_bar0_rw_offset(
struct spear_pcie_gadget_config *config,
char *buf)
{
return sprintf(buf, "%lx", config->bar0_rw_offset);
}
static ssize_t pcie_gadget_store_bar0_rw_offset(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
ulong offset;
if (strict_strtoul(buf, 0, &offset))
return -EINVAL;
if (offset % 4)
return -EINVAL;
config->bar0_rw_offset = offset;
return count;
}
static ssize_t pcie_gadget_show_bar0_data(
struct spear_pcie_gadget_config *config,
char *buf)
{
ulong data;
if (!config->va_bar0_address)
return -ENOMEM;
data = readl((ulong)config->va_bar0_address + config->bar0_rw_offset);
return sprintf(buf, "%lx", data);
}
static ssize_t pcie_gadget_store_bar0_data(
struct spear_pcie_gadget_config *config,
const char *buf, size_t count)
{
ulong data;
if (strict_strtoul(buf, 0, &data))
return -EINVAL;
if (!config->va_bar0_address)
return -ENOMEM;
writel(data, (ulong)config->va_bar0_address + config->bar0_rw_offset);
return count;
}
/*
* Attribute definitions.
*/
#define PCIE_GADGET_TARGET_ATTR_RO(_name) \
static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
__CONFIGFS_ATTR(_name, S_IRUGO, pcie_gadget_show_##_name, NULL)
#define PCIE_GADGET_TARGET_ATTR_WO(_name) \
static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
__CONFIGFS_ATTR(_name, S_IWUSR, NULL, pcie_gadget_store_##_name)
#define PCIE_GADGET_TARGET_ATTR_RW(_name) \
static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
__CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, pcie_gadget_show_##_name, \
pcie_gadget_store_##_name)
PCIE_GADGET_TARGET_ATTR_RW(link);
PCIE_GADGET_TARGET_ATTR_RW(int_type);
PCIE_GADGET_TARGET_ATTR_RW(no_of_msi);
PCIE_GADGET_TARGET_ATTR_WO(inta);
PCIE_GADGET_TARGET_ATTR_WO(send_msi);
PCIE_GADGET_TARGET_ATTR_RW(vendor_id);
PCIE_GADGET_TARGET_ATTR_RW(device_id);
PCIE_GADGET_TARGET_ATTR_RW(bar0_size);
PCIE_GADGET_TARGET_ATTR_RW(bar0_address);
PCIE_GADGET_TARGET_ATTR_RW(bar0_rw_offset);
PCIE_GADGET_TARGET_ATTR_RW(bar0_data);
static struct configfs_attribute *pcie_gadget_target_attrs[] = {
&pcie_gadget_target_link.attr,
&pcie_gadget_target_int_type.attr,
&pcie_gadget_target_no_of_msi.attr,
&pcie_gadget_target_inta.attr,
&pcie_gadget_target_send_msi.attr,
&pcie_gadget_target_vendor_id.attr,
&pcie_gadget_target_device_id.attr,
&pcie_gadget_target_bar0_size.attr,
&pcie_gadget_target_bar0_address.attr,
&pcie_gadget_target_bar0_rw_offset.attr,
&pcie_gadget_target_bar0_data.attr,
NULL,
};
static struct pcie_gadget_target *to_target(struct config_item *item)
{
return item ?
container_of(to_configfs_subsystem(to_config_group(item)),
struct pcie_gadget_target, subsys) : NULL;
}
/*
* Item operations and type for pcie_gadget_target.
*/
static ssize_t pcie_gadget_target_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *buf)
{
ssize_t ret = -EINVAL;
struct pcie_gadget_target *target = to_target(item);
struct pcie_gadget_target_attr *t_attr =
container_of(attr, struct pcie_gadget_target_attr, attr);
if (t_attr->show)
ret = t_attr->show(&target->config, buf);
return ret;
}
static ssize_t pcie_gadget_target_attr_store(struct config_item *item,
struct configfs_attribute *attr,
const char *buf,
size_t count)
{
ssize_t ret = -EINVAL;
struct pcie_gadget_target *target = to_target(item);
struct pcie_gadget_target_attr *t_attr =
container_of(attr, struct pcie_gadget_target_attr, attr);
if (t_attr->store)
ret = t_attr->store(&target->config, buf, count);
return ret;
}
static struct configfs_item_operations pcie_gadget_target_item_ops = {
.show_attribute = pcie_gadget_target_attr_show,
.store_attribute = pcie_gadget_target_attr_store,
};
static struct config_item_type pcie_gadget_target_type = {
.ct_attrs = pcie_gadget_target_attrs,
.ct_item_ops = &pcie_gadget_target_item_ops,
.ct_owner = THIS_MODULE,
};
static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
/*setup registers for outbound translation */
writel(config->base, &app_reg->in0_mem_addr_start);
writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
&app_reg->in0_mem_addr_limit);
writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
&app_reg->in1_mem_addr_limit);
writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
writel(app_reg->in_io_addr_start + IN_IO_SIZE,
&app_reg->in_io_addr_limit);
writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
&app_reg->in_cfg0_addr_limit);
writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
&app_reg->in_cfg1_addr_limit);
writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
&app_reg->in_msg_addr_limit);
writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
/*setup registers for inbound translation */
/* Keep AORAM mapped at BAR0 as default */
config->bar0_size = INBOUND_ADDR_MASK + 1;
spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
spear_dbi_write_reg(config, PCI_BASE_ADDRESS_0, 4, 0xC);
config->va_bar0_address = ioremap(SPEAR13XX_SYSRAM1_BASE,
config->bar0_size);
writel(SPEAR13XX_SYSRAM1_BASE, &app_reg->pim0_mem_addr_start);
writel(0, &app_reg->pim1_mem_addr_start);
writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
writel(0x0, &app_reg->pim_io_addr_start);
writel(0x0, &app_reg->pim_io_addr_start);
writel(0x0, &app_reg->pim_rom_addr_start);
writel(DEVICE_TYPE_EP | (1 << MISCTRL_EN_ID)
| ((u32)1 << REG_TRANSLATION_ENABLE),
&app_reg->app_ctrl_0);
/* disable all rx interrupts */
writel(0, &app_reg->int_mask);
/* Select INTA as default*/
spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
}
static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
{
struct resource *res0, *res1;
unsigned int status = 0;
int irq;
struct clk *clk;
static struct pcie_gadget_target *target;
struct spear_pcie_gadget_config *config;
struct config_item *cg_item;
struct configfs_subsystem *subsys;
/* get resource for application registers*/
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res0) {
dev_err(&pdev->dev, "no resource defined\n");
return -EBUSY;
}
if (!request_mem_region(res0->start, resource_size(res0),
pdev->name)) {
dev_err(&pdev->dev, "pcie gadget region already claimed\n");
return -EBUSY;
}
/* get resource for dbi registers*/
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res1) {
dev_err(&pdev->dev, "no resource defined\n");
goto err_rel_res0;
}
if (!request_mem_region(res1->start, resource_size(res1),
pdev->name)) {
dev_err(&pdev->dev, "pcie gadget region already claimed\n");
goto err_rel_res0;
}
target = kzalloc(sizeof(*target), GFP_KERNEL);
if (!target) {
dev_err(&pdev->dev, "out of memory\n");
status = -ENOMEM;
goto err_rel_res;
}
cg_item = &target->subsys.su_group.cg_item;
sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id);
cg_item->ci_type = &pcie_gadget_target_type;
config = &target->config;
config->va_app_base = (void __iomem *)ioremap(res0->start,
resource_size(res0));
if (!config->va_app_base) {
dev_err(&pdev->dev, "ioremap fail\n");
status = -ENOMEM;
goto err_kzalloc;
}
config->base = (void __iomem *)res1->start;
config->va_dbi_base = (void __iomem *)ioremap(res1->start,
resource_size(res1));
if (!config->va_dbi_base) {
dev_err(&pdev->dev, "ioremap fail\n");
status = -ENOMEM;
goto err_iounmap_app;
}
dev_set_drvdata(&pdev->dev, target);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no update irq?\n");
status = irq;
goto err_iounmap;
}
status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL);
if (status) {
dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \
claimed\n", irq);
goto err_iounmap;
}
/* Register configfs hooks */
subsys = &target->subsys;
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
status = configfs_register_subsystem(subsys);
if (status)
goto err_irq;
/*
* init basic pcie application registers
* do not enable clock if it is PCIE0.Ideally , all controller should
* have been independent from others with respect to clock. But PCIE1
* and 2 depends on PCIE0.So PCIE0 clk is provided during board init.
*/
if (pdev->id == 1) {
/*
* Ideally CFG Clock should have been also enabled here. But
* it is done currently during board init routne
*/
clk = clk_get_sys("pcie1", NULL);
if (IS_ERR(clk)) {
pr_err("%s:couldn't get clk for pcie1\n", __func__);
goto err_irq;
}
if (clk_enable(clk)) {
pr_err("%s:couldn't enable clk for pcie1\n", __func__);
goto err_irq;
}
} else if (pdev->id == 2) {
/*
* Ideally CFG Clock should have been also enabled here. But
* it is done currently during board init routne
*/
clk = clk_get_sys("pcie2", NULL);
if (IS_ERR(clk)) {
pr_err("%s:couldn't get clk for pcie2\n", __func__);
goto err_irq;
}
if (clk_enable(clk)) {
pr_err("%s:couldn't enable clk for pcie2\n", __func__);
goto err_irq;
}
}
spear13xx_pcie_device_init(config);
return 0;
err_irq:
free_irq(irq, NULL);
err_iounmap:
iounmap(config->va_dbi_base);
err_iounmap_app:
iounmap(config->va_app_base);
err_kzalloc:
kfree(config);
err_rel_res:
release_mem_region(res1->start, resource_size(res1));
err_rel_res0:
release_mem_region(res0->start, resource_size(res0));
return status;
}
static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev)
{
struct resource *res0, *res1;
static struct pcie_gadget_target *target;
struct spear_pcie_gadget_config *config;
int irq;
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
irq = platform_get_irq(pdev, 0);
target = dev_get_drvdata(&pdev->dev);
config = &target->config;
free_irq(irq, NULL);
iounmap(config->va_dbi_base);
iounmap(config->va_app_base);
release_mem_region(res1->start, resource_size(res1));
release_mem_region(res0->start, resource_size(res0));
configfs_unregister_subsystem(&target->subsys);
kfree(target);
return 0;
}
static void spear_pcie_gadget_shutdown(struct platform_device *pdev)
{
}
static struct platform_driver spear_pcie_gadget_driver = {
.probe = spear_pcie_gadget_probe,
.remove = spear_pcie_gadget_remove,
.shutdown = spear_pcie_gadget_shutdown,
.driver = {
.name = "pcie-gadget-spear",
.bus = &platform_bus_type
},
};
static int __init spear_pcie_gadget_init(void)
{
return platform_driver_register(&spear_pcie_gadget_driver);
}
module_init(spear_pcie_gadget_init);
static void __exit spear_pcie_gadget_exit(void)
{
platform_driver_unregister(&spear_pcie_gadget_driver);
}
module_exit(spear_pcie_gadget_exit);
MODULE_ALIAS("pcie-gadget-spear");
MODULE_AUTHOR("Pratyush Anand");
MODULE_LICENSE("GPL");

View File

@@ -1417,7 +1417,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
if (res == NULL || irq < 0)
return -ENXIO;
res = request_mem_region(res->start, res->end - res->start + 1,
res = request_mem_region(res->start, resource_size(res),
pdev->name);
if (res == NULL)
return -EBUSY;
@@ -1457,7 +1457,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->irq = irq;
host->phys_base = host->mem_res->start;
host->virt_base = ioremap(res->start, res->end - res->start + 1);
host->virt_base = ioremap(res->start, resource_size(res));
if (!host->virt_base)
goto err_ioremap;
@@ -1514,7 +1514,7 @@ err_free_mmc_host:
err_ioremap:
kfree(host);
err_free_mem_region:
release_mem_region(res->start, res->end - res->start + 1);
release_mem_region(res->start, resource_size(res));
return ret;
}

View File

@@ -2047,8 +2047,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
res->start += pdata->reg_offset;
res->end += pdata->reg_offset;
res = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (res == NULL)
return -EBUSY;
@@ -2287,7 +2286,7 @@ err1:
err_alloc:
omap_hsmmc_gpio_free(pdata);
err:
release_mem_region(res->start, res->end - res->start + 1);
release_mem_region(res->start, resource_size(res));
return ret;
}
@@ -2324,7 +2323,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, res->end - res->start + 1);
release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
return 0;

View File

@@ -2130,7 +2130,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
}
/*
* First release a slave and than destroy the bond if no more slaves are left.
* First release a slave and then destroy the bond if no more slaves are left.
* Must be under rtnl_lock when this function is called.
*/
static int bond_release_and_destroy(struct net_device *bond_dev,

View File

@@ -633,9 +633,6 @@ static void c_can_start(struct net_device *dev)
{
struct c_can_priv *priv = netdev_priv(dev);
/* enable status change, error and module interrupts */
c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS);
/* basic c_can configuration */
c_can_chip_config(dev);
@@ -643,6 +640,9 @@ static void c_can_start(struct net_device *dev)
/* reset tx helper pointers */
priv->tx_next = priv->tx_echo = 0;
/* enable status change, error and module interrupts */
c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS);
}
static void c_can_stop(struct net_device *dev)

View File

@@ -1102,7 +1102,7 @@ static int ftmac100_probe(struct platform_device *pdev)
goto err_req_mem;
}
priv->base = ioremap(res->start, res->end - res->start);
priv->base = ioremap(res->start, resource_size(res));
if (!priv->base) {
dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
err = -EIO;

View File

@@ -949,6 +949,11 @@ static void gfar_detect_errata(struct gfar_private *priv)
(pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
priv->errata |= GFAR_ERRATA_A002;
/* MPC8313 Rev < 2.0, MPC8548 rev 2.0 */
if ((pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020) ||
(pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020))
priv->errata |= GFAR_ERRATA_12;
if (priv->errata)
dev_info(dev, "enabled errata workarounds, flags: 0x%x\n",
priv->errata);
@@ -2154,8 +2159,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Set up checksumming */
if (CHECKSUM_PARTIAL == skb->ip_summed) {
fcb = gfar_add_fcb(skb);
lstatus |= BD_LFLAG(TXBD_TOE);
gfar_tx_checksum(skb, fcb);
/* as specified by errata */
if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_12)
&& ((unsigned long)fcb % 0x20) > 0x18)) {
__skb_pull(skb, GMAC_FCB_LEN);
skb_checksum_help(skb);
} else {
lstatus |= BD_LFLAG(TXBD_TOE);
gfar_tx_checksum(skb, fcb);
}
}
if (vlan_tx_tag_present(skb)) {

View File

@@ -1039,6 +1039,7 @@ enum gfar_errata {
GFAR_ERRATA_74 = 0x01,
GFAR_ERRATA_76 = 0x02,
GFAR_ERRATA_A002 = 0x04,
GFAR_ERRATA_12 = 0x08, /* a.k.a errata eTSEC49 */
};
/* Struct stolen almost completely (and shamelessly) from the FCC enet source

View File

@@ -39,8 +39,11 @@ struct macvlan_port {
struct list_head vlans;
struct rcu_head rcu;
bool passthru;
int count;
};
static void macvlan_port_destroy(struct net_device *dev);
#define macvlan_port_get_rcu(dev) \
((struct macvlan_port *) rcu_dereference(dev->rx_handler_data))
#define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data)
@@ -457,8 +460,13 @@ static int macvlan_init(struct net_device *dev)
static void macvlan_uninit(struct net_device *dev)
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
free_percpu(vlan->pcpu_stats);
port->count -= 1;
if (!port->count)
macvlan_port_destroy(port->dev);
}
static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev,
@@ -691,12 +699,13 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
if (!list_empty(&port->vlans))
if (port->count)
return -EINVAL;
port->passthru = true;
memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN);
}
port->count += 1;
err = register_netdevice(dev);
if (err < 0)
goto destroy_port;
@@ -707,7 +716,8 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
return 0;
destroy_port:
if (list_empty(&port->vlans))
port->count -= 1;
if (!port->count)
macvlan_port_destroy(lowerdev);
return err;
@@ -725,13 +735,9 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev,
void macvlan_dellink(struct net_device *dev, struct list_head *head)
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
list_del(&vlan->list);
unregister_netdevice_queue(dev, head);
if (list_empty(&port->vlans))
macvlan_port_destroy(port->dev);
}
EXPORT_SYMBOL_GPL(macvlan_dellink);

View File

@@ -9501,7 +9501,7 @@ static struct niu_parent * __devinit niu_new_parent(struct niu *np,
struct niu_parent *p;
int i;
plat_dev = platform_device_register_simple("niu", niu_parent_index,
plat_dev = platform_device_register_simple("niu-board", niu_parent_index,
NULL, 0);
if (IS_ERR(plat_dev))
return NULL;

View File

@@ -129,7 +129,7 @@ static void *z_comp_alloc(unsigned char *options, int opt_len)
state->strm.next_in = NULL;
state->w_size = w_size;
state->strm.workspace = vmalloc(zlib_deflate_workspacesize());
state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8));
if (state->strm.workspace == NULL)
goto out_free;

View File

@@ -171,7 +171,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb->ip_summed == CHECKSUM_NONE)
skb->ip_summed = rcv_priv->ip_summed;
length = skb->len + ETH_HLEN;
length = skb->len;
if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS)
goto rx_drop;

View File

@@ -1031,6 +1031,7 @@ static int __devinit acer_backlight_init(struct device *dev)
struct backlight_device *bd;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = max_brightness;
bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops,
&props);

View File

@@ -667,6 +667,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 15;
props.type = BACKLIGHT_PLATFORM;
bd = backlight_device_register(ASUS_LAPTOP_FILE,
&asus->platform_device->dev, asus,

View File

@@ -1507,6 +1507,7 @@ static int __init asus_acpi_init(void)
}
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = 15;
asus_backlight_device = backlight_device_register("asus", NULL, NULL,
&asus_backlight_data,

View File

@@ -564,6 +564,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi)
return -ENOMEM;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = 7;
ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
acpi->handle, &cmpc_bl_ops,

View File

@@ -970,6 +970,7 @@ static int __init compal_init(void)
if (!acpi_video_backlight_support()) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = BACKLIGHT_LEVEL_MAX;
compalbl_device = backlight_device_register(DRIVER_NAME,
NULL, NULL,

View File

@@ -671,6 +671,7 @@ static int __init dell_init(void)
if (max_intensity) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = max_intensity;
dell_backlight_device = backlight_device_register("dell_backlight",
&platform_device->dev,

View File

@@ -1147,6 +1147,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
struct backlight_device *bd;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = 15;
bd = backlight_device_register(EEEPC_LAPTOP_FILE,
&eeepc->platform_device->dev, eeepc,

View File

@@ -1128,6 +1128,7 @@ static int __init fujitsu_init(void)
memset(&props, 0, sizeof(struct backlight_properties));
max_brightness = fujitsu->max_brightness;
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = max_brightness - 1;
fujitsu->bl_device = backlight_device_register("fujitsu-laptop",
NULL, NULL,

View File

@@ -804,6 +804,7 @@ static int __init msi_init(void)
} else {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
msibl_device = backlight_device_register("msi-laptop-bl", NULL,
NULL, &msibl_ops,

View File

@@ -254,6 +254,7 @@ static int __init msi_wmi_init(void)
if (!acpi_video_backlight_support()) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
backlight = backlight_device_register(DRV_NAME, NULL, NULL,
&msi_backlight_ops,

View File

@@ -602,6 +602,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
}
/* initialize backlight */
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];
pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
&pcc_backlight_ops, &props);

View File

@@ -1305,8 +1305,9 @@ static int sony_nc_add(struct acpi_device *device)
"controlled by ACPI video driver\n");
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
&handle))) {
struct backlight_properties props;
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = SONY_MAX_BRIGHTNESS - 1;
sony_backlight_device = backlight_device_register("sony", NULL,
NULL,

View File

@@ -6307,6 +6307,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
return 1;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = bright_maxlvl;
props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME,

View File

@@ -1018,6 +1018,7 @@ static int __init toshiba_acpi_init(void)
create_toshiba_proc_entries();
}
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
toshiba_backlight_device = backlight_device_register("toshiba",
&toshiba_acpi.p_dev->dev,

View File

@@ -142,7 +142,9 @@ void __pnp_remove_device(struct pnp_dev *dev);
int pnp_check_port(struct pnp_dev *dev, struct resource *res);
int pnp_check_mem(struct pnp_dev *dev, struct resource *res);
int pnp_check_irq(struct pnp_dev *dev, struct resource *res);
#ifdef CONFIG_ISA_DMA_API
int pnp_check_dma(struct pnp_dev *dev, struct resource *res);
#endif
char *pnp_resource_type_name(struct resource *res);
void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc);

View File

@@ -171,6 +171,7 @@ __add:
return 0;
}
#ifdef CONFIG_ISA_DMA_API
static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
{
struct resource *res, local_res;
@@ -210,6 +211,7 @@ __add:
pnp_add_dma_resource(dev, res->start, res->flags);
return 0;
}
#endif /* CONFIG_ISA_DMA_API */
void pnp_init_resources(struct pnp_dev *dev)
{
@@ -234,7 +236,8 @@ static void pnp_clean_resource_table(struct pnp_dev *dev)
static int pnp_assign_resources(struct pnp_dev *dev, int set)
{
struct pnp_option *option;
int nport = 0, nmem = 0, nirq = 0, ndma = 0;
int nport = 0, nmem = 0, nirq = 0;
int ndma __maybe_unused = 0;
int ret = 0;
pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);
@@ -256,9 +259,11 @@ static int pnp_assign_resources(struct pnp_dev *dev, int set)
case IORESOURCE_IRQ:
ret = pnp_assign_irq(dev, &option->u.irq, nirq++);
break;
#ifdef CONFIG_ISA_DMA_API
case IORESOURCE_DMA:
ret = pnp_assign_dma(dev, &option->u.dma, ndma++);
break;
#endif
default:
ret = -EINVAL;
break;

View File

@@ -409,9 +409,9 @@ int pnp_check_irq(struct pnp_dev *dev, struct resource *res)
return 1;
}
#ifdef CONFIG_ISA_DMA_API
int pnp_check_dma(struct pnp_dev *dev, struct resource *res)
{
#ifndef CONFIG_IA64
int i;
struct pnp_dev *tdev;
struct resource *tres;
@@ -466,11 +466,8 @@ int pnp_check_dma(struct pnp_dev *dev, struct resource *res)
}
return 1;
#else
/* IA64 does not have legacy DMA */
return 0;
#endif
}
#endif /* CONFIG_ISA_DMA_API */
unsigned long pnp_resource_type(struct resource *res)
{

View File

@@ -216,11 +216,6 @@ static void parport_attach(struct parport *port)
hrtimer_init(&device.timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
device.timer.function = hrtimer_event;
#ifdef CONFIG_PREEMPT_RT
/* hrtimer interrupt will run in the interrupt context with this */
device.timer.irqsafe = 1;
#endif
hrtimer_start(&device.timer, next_intr_time(&device), HRTIMER_MODE_ABS);
return;

View File

@@ -985,4 +985,14 @@ config RTC_DRV_LPC32XX
This driver can also be buillt as a module. If so, the module
will be called rtc-lpc32xx.
config RTC_DRV_TEGRA
tristate "NVIDIA Tegra Internal RTC driver"
depends on RTC_CLASS && ARCH_TEGRA
help
If you say yes here you get support for the
Tegra 200 series internal RTC module.
This drive can also be built as a module. If so, the module
will be called rtc-tegra.
endif # RTC_CLASS

View File

@@ -91,6 +91,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o

View File

@@ -25,6 +25,7 @@
#include <linux/bcd.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/pm.h>
#define DS1374_REG_TOD0 0x00 /* Time of Day */
#define DS1374_REG_TOD1 0x01
@@ -409,32 +410,38 @@ static int __devexit ds1374_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
static int ds1374_suspend(struct i2c_client *client, pm_message_t state)
static int ds1374_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (client->irq >= 0 && device_may_wakeup(&client->dev))
enable_irq_wake(client->irq);
return 0;
}
static int ds1374_resume(struct i2c_client *client)
static int ds1374_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (client->irq >= 0 && device_may_wakeup(&client->dev))
disable_irq_wake(client->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
#define DS1374_PM (&ds1374_pm)
#else
#define ds1374_suspend NULL
#define ds1374_resume NULL
#define DS1374_PM NULL
#endif
static struct i2c_driver ds1374_driver = {
.driver = {
.name = "rtc-ds1374",
.owner = THIS_MODULE,
.pm = DS1374_PM,
},
.probe = ds1374_probe,
.suspend = ds1374_suspend,
.resume = ds1374_resume,
.remove = __devexit_p(ds1374_remove),
.id_table = ds1374_id,
};

View File

@@ -468,7 +468,7 @@ ds1511_nvram_write(struct file *filp, struct kobject *kobj,
static struct bin_attribute ds1511_nvram_attr = {
.attr = {
.name = "nvram",
.mode = S_IRUGO | S_IWUGO,
.mode = S_IRUGO | S_IWUSR,
},
.size = DS1511_RAM_MAX,
.read = ds1511_nvram_read,

View File

@@ -39,6 +39,8 @@
#define ISL1208_REG_SR_BAT (1<<1) /* battery */
#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
#define ISL1208_REG_INT 0x08
#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */
#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */
#define ISL1208_REG_09 0x09 /* reserved */
#define ISL1208_REG_ATR 0x0a
#define ISL1208_REG_DTR 0x0b
@@ -201,6 +203,30 @@ isl1208_i2c_set_usr(struct i2c_client *client, u16 usr)
ISL1208_USR_SECTION_LEN);
}
static int
isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable)
{
int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
if (icr < 0) {
dev_err(&client->dev, "%s: reading INT failed\n", __func__);
return icr;
}
if (enable)
icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM;
else
icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM);
icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr);
if (icr < 0) {
dev_err(&client->dev, "%s: writing INT failed\n", __func__);
return icr;
}
return 0;
}
static int
isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
{
@@ -288,9 +314,8 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
{
struct rtc_time *const tm = &alarm->time;
u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
int sr;
int icr, yr, sr = isl1208_i2c_get_sr(client);
sr = isl1208_i2c_get_sr(client);
if (sr < 0) {
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
return sr;
@@ -313,6 +338,73 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1;
tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03);
/* The alarm doesn't store the year so get it from the rtc section */
yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR);
if (yr < 0) {
dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__);
return yr;
}
tm->tm_year = bcd2bin(yr) + 100;
icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
if (icr < 0) {
dev_err(&client->dev, "%s: reading INT failed\n", __func__);
return icr;
}
alarm->enabled = !!(icr & ISL1208_REG_INT_ALME);
return 0;
}
static int
isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
{
struct rtc_time *alarm_tm = &alarm->time;
u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
const int offs = ISL1208_REG_SCA;
unsigned long rtc_secs, alarm_secs;
struct rtc_time rtc_tm;
int err, enable;
err = isl1208_i2c_read_time(client, &rtc_tm);
if (err)
return err;
err = rtc_tm_to_time(&rtc_tm, &rtc_secs);
if (err)
return err;
err = rtc_tm_to_time(alarm_tm, &alarm_secs);
if (err)
return err;
/* If the alarm time is before the current time disable the alarm */
if (!alarm->enabled || alarm_secs <= rtc_secs)
enable = 0x00;
else
enable = 0x80;
/* Program the alarm and enable it for each setting */
regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable;
regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable;
regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) |
ISL1208_REG_HR_MIL | enable;
regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable;
regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable;
regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable;
/* write ALARM registers */
err = isl1208_i2c_set_regs(client, offs, regs,
ISL1208_ALARM_SECTION_LEN);
if (err < 0) {
dev_err(&client->dev, "%s: writing ALARM section failed\n",
__func__);
return err;
}
err = isl1208_rtc_toggle_alarm(client, enable);
if (err)
return err;
return 0;
}
@@ -391,12 +483,63 @@ isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
}
static int
isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
}
static irqreturn_t
isl1208_rtc_interrupt(int irq, void *data)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
struct i2c_client *client = data;
int handled = 0, sr, err;
/*
* I2C reads get NAK'ed if we read straight away after an interrupt?
* Using a mdelay/msleep didn't seem to help either, so we work around
* this by continually trying to read the register for a short time.
*/
while (1) {
sr = isl1208_i2c_get_sr(client);
if (sr >= 0)
break;
if (time_after(jiffies, timeout)) {
dev_err(&client->dev, "%s: reading SR failed\n",
__func__);
return sr;
}
}
if (sr & ISL1208_REG_SR_ALM) {
dev_dbg(&client->dev, "alarm!\n");
/* Clear the alarm */
sr &= ~ISL1208_REG_SR_ALM;
sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
if (sr < 0)
dev_err(&client->dev, "%s: writing SR failed\n",
__func__);
else
handled = 1;
/* Disable the alarm */
err = isl1208_rtc_toggle_alarm(client, 0);
if (err)
return err;
}
return handled ? IRQ_HANDLED : IRQ_NONE;
}
static const struct rtc_class_ops isl1208_rtc_ops = {
.proc = isl1208_rtc_proc,
.read_time = isl1208_rtc_read_time,
.set_time = isl1208_rtc_set_time,
.read_alarm = isl1208_rtc_read_alarm,
/*.set_alarm = isl1208_rtc_set_alarm, */
.set_alarm = isl1208_rtc_set_alarm,
};
/* sysfs interface */
@@ -488,11 +631,29 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_info(&client->dev,
"chip found, driver version " DRV_VERSION "\n");
if (client->irq > 0) {
rc = request_threaded_irq(client->irq, NULL,
isl1208_rtc_interrupt,
IRQF_SHARED,
isl1208_driver.driver.name, client);
if (!rc) {
device_init_wakeup(&client->dev, 1);
enable_irq_wake(client->irq);
} else {
dev_err(&client->dev,
"Unable to request irq %d, no alarm support\n",
client->irq);
client->irq = 0;
}
}
rtc = rtc_device_register(isl1208_driver.driver.name,
&client->dev, &isl1208_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
if (IS_ERR(rtc)) {
rc = PTR_ERR(rtc);
goto exit_free_irq;
}
i2c_set_clientdata(client, rtc);
@@ -514,6 +675,9 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
exit_unregister:
rtc_device_unregister(rtc);
exit_free_irq:
if (client->irq)
free_irq(client->irq, client);
return rc;
}
@@ -525,6 +689,8 @@ isl1208_remove(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
rtc_device_unregister(rtc);
if (client->irq)
free_irq(client->irq, client);
return 0;
}

488
drivers/rtc/rtc-tegra.c Normal file
View File

@@ -0,0 +1,488 @@
/*
* An RTC driver for the NVIDIA Tegra 200 series internal RTC.
*
* Copyright (c) 2010, NVIDIA Corporation.
*
* 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 <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
/* set to 1 = busy every eight 32kHz clocks during copy of sec+msec to AHB */
#define TEGRA_RTC_REG_BUSY 0x004
#define TEGRA_RTC_REG_SECONDS 0x008
/* when msec is read, the seconds are buffered into shadow seconds. */
#define TEGRA_RTC_REG_SHADOW_SECONDS 0x00c
#define TEGRA_RTC_REG_MILLI_SECONDS 0x010
#define TEGRA_RTC_REG_SECONDS_ALARM0 0x014
#define TEGRA_RTC_REG_SECONDS_ALARM1 0x018
#define TEGRA_RTC_REG_MILLI_SECONDS_ALARM0 0x01c
#define TEGRA_RTC_REG_INTR_MASK 0x028
/* write 1 bits to clear status bits */
#define TEGRA_RTC_REG_INTR_STATUS 0x02c
/* bits in INTR_MASK */
#define TEGRA_RTC_INTR_MASK_MSEC_CDN_ALARM (1<<4)
#define TEGRA_RTC_INTR_MASK_SEC_CDN_ALARM (1<<3)
#define TEGRA_RTC_INTR_MASK_MSEC_ALARM (1<<2)
#define TEGRA_RTC_INTR_MASK_SEC_ALARM1 (1<<1)
#define TEGRA_RTC_INTR_MASK_SEC_ALARM0 (1<<0)
/* bits in INTR_STATUS */
#define TEGRA_RTC_INTR_STATUS_MSEC_CDN_ALARM (1<<4)
#define TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM (1<<3)
#define TEGRA_RTC_INTR_STATUS_MSEC_ALARM (1<<2)
#define TEGRA_RTC_INTR_STATUS_SEC_ALARM1 (1<<1)
#define TEGRA_RTC_INTR_STATUS_SEC_ALARM0 (1<<0)
struct tegra_rtc_info {
struct platform_device *pdev;
struct rtc_device *rtc_dev;
void __iomem *rtc_base; /* NULL if not initialized. */
int tegra_rtc_irq; /* alarm and periodic irq */
spinlock_t tegra_rtc_lock;
};
/* RTC hardware is busy when it is updating its values over AHB once
* every eight 32kHz clocks (~250uS).
* outside of these updates the CPU is free to write.
* CPU is always free to read.
*/
static inline u32 tegra_rtc_check_busy(struct tegra_rtc_info *info)
{
return readl(info->rtc_base + TEGRA_RTC_REG_BUSY) & 1;
}
/* Wait for hardware to be ready for writing.
* This function tries to maximize the amount of time before the next update.
* It does this by waiting for the RTC to become busy with its periodic update,
* then returning once the RTC first becomes not busy.
* This periodic update (where the seconds and milliseconds are copied to the
* AHB side) occurs every eight 32kHz clocks (~250uS).
* The behavior of this function allows us to make some assumptions without
* introducing a race, because 250uS is plenty of time to read/write a value.
*/
static int tegra_rtc_wait_while_busy(struct device *dev)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
int retries = 500; /* ~490 us is the worst case, ~250 us is best. */
/* first wait for the RTC to become busy. this is when it
* posts its updated seconds+msec registers to AHB side. */
while (tegra_rtc_check_busy(info)) {
if (!retries--)
goto retry_failed;
udelay(1);
}
/* now we have about 250 us to manipulate registers */
return 0;
retry_failed:
dev_err(dev, "write failed:retry count exceeded.\n");
return -ETIMEDOUT;
}
static int tegra_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
unsigned long sec, msec;
unsigned long sl_irq_flags;
/* RTC hardware copies seconds to shadow seconds when a read
* of milliseconds occurs. use a lock to keep other threads out. */
spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
msec = readl(info->rtc_base + TEGRA_RTC_REG_MILLI_SECONDS);
sec = readl(info->rtc_base + TEGRA_RTC_REG_SHADOW_SECONDS);
spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
rtc_time_to_tm(sec, tm);
dev_vdbg(dev, "time read as %lu. %d/%d/%d %d:%02u:%02u\n",
sec,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec
);
return 0;
}
static int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
unsigned long sec;
int ret;
/* convert tm to seconds. */
ret = rtc_valid_tm(tm);
if (ret)
return ret;
rtc_tm_to_time(tm, &sec);
dev_vdbg(dev, "time set to %lu. %d/%d/%d %d:%02u:%02u\n",
sec,
tm->tm_mon+1,
tm->tm_mday,
tm->tm_year+1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec
);
/* seconds only written if wait succeeded. */
ret = tegra_rtc_wait_while_busy(dev);
if (!ret)
writel(sec, info->rtc_base + TEGRA_RTC_REG_SECONDS);
dev_vdbg(dev, "time read back as %d\n",
readl(info->rtc_base + TEGRA_RTC_REG_SECONDS));
return ret;
}
static int tegra_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
unsigned long sec;
unsigned tmp;
sec = readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
if (sec == 0) {
/* alarm is disabled. */
alarm->enabled = 0;
alarm->time.tm_mon = -1;
alarm->time.tm_mday = -1;
alarm->time.tm_year = -1;
alarm->time.tm_hour = -1;
alarm->time.tm_min = -1;
alarm->time.tm_sec = -1;
} else {
/* alarm is enabled. */
alarm->enabled = 1;
rtc_time_to_tm(sec, &alarm->time);
}
tmp = readl(info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
alarm->pending = (tmp & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) != 0;
return 0;
}
static int tegra_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
unsigned status;
unsigned long sl_irq_flags;
tegra_rtc_wait_while_busy(dev);
spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
/* read the original value, and OR in the flag. */
status = readl(info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
if (enabled)
status |= TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* set it */
else
status &= ~TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* clear it */
writel(status, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
return 0;
}
static int tegra_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct tegra_rtc_info *info = dev_get_drvdata(dev);
unsigned long sec;
if (alarm->enabled)
rtc_tm_to_time(&alarm->time, &sec);
else
sec = 0;
tegra_rtc_wait_while_busy(dev);
writel(sec, info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
dev_vdbg(dev, "alarm read back as %d\n",
readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0));
/* if successfully written and alarm is enabled ... */
if (sec) {
tegra_rtc_alarm_irq_enable(dev, 1);
dev_vdbg(dev, "alarm set as %lu. %d/%d/%d %d:%02u:%02u\n",
sec,
alarm->time.tm_mon+1,
alarm->time.tm_mday,
alarm->time.tm_year+1900,
alarm->time.tm_hour,
alarm->time.tm_min,
alarm->time.tm_sec);
} else {
/* disable alarm if 0 or write error. */
dev_vdbg(dev, "alarm disabled\n");
tegra_rtc_alarm_irq_enable(dev, 0);
}
return 0;
}
static int tegra_rtc_proc(struct device *dev, struct seq_file *seq)
{
if (!dev || !dev->driver)
return 0;
return seq_printf(seq, "name\t\t: %s\n", dev_name(dev));
}
static irqreturn_t tegra_rtc_irq_handler(int irq, void *data)
{
struct device *dev = data;
struct tegra_rtc_info *info = dev_get_drvdata(dev);
unsigned long events = 0;
unsigned status;
unsigned long sl_irq_flags;
status = readl(info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
if (status) {
/* clear the interrupt masks and status on any irq. */
tegra_rtc_wait_while_busy(dev);
spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
writel(0, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
writel(status, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
}
/* check if Alarm */
if ((status & TEGRA_RTC_INTR_STATUS_SEC_ALARM0))
events |= RTC_IRQF | RTC_AF;
/* check if Periodic */
if ((status & TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM))
events |= RTC_IRQF | RTC_PF;
rtc_update_irq(info->rtc_dev, 1, events);
return IRQ_HANDLED;
}
static struct rtc_class_ops tegra_rtc_ops = {
.read_time = tegra_rtc_read_time,
.set_time = tegra_rtc_set_time,
.read_alarm = tegra_rtc_read_alarm,
.set_alarm = tegra_rtc_set_alarm,
.proc = tegra_rtc_proc,
.alarm_irq_enable = tegra_rtc_alarm_irq_enable,
};
static int __devinit tegra_rtc_probe(struct platform_device *pdev)
{
struct tegra_rtc_info *info;
struct resource *res;
int ret;
info = kzalloc(sizeof(struct tegra_rtc_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev,
"Unable to allocate resources for device.\n");
ret = -EBUSY;
goto err_free_info;
}
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
dev_err(&pdev->dev,
"Unable to request mem region for device.\n");
ret = -EBUSY;
goto err_free_info;
}
info->tegra_rtc_irq = platform_get_irq(pdev, 0);
if (info->tegra_rtc_irq <= 0) {
ret = -EBUSY;
goto err_release_mem_region;
}
info->rtc_base = ioremap_nocache(res->start, resource_size(res));
if (!info->rtc_base) {
dev_err(&pdev->dev, "Unable to grab IOs for device.\n");
ret = -EBUSY;
goto err_release_mem_region;
}
/* set context info. */
info->pdev = pdev;
info->tegra_rtc_lock = __SPIN_LOCK_UNLOCKED(info->tegra_rtc_lock);
platform_set_drvdata(pdev, info);
/* clear out the hardware. */
writel(0, info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
writel(0xffffffff, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
writel(0, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
device_init_wakeup(&pdev->dev, 1);
info->rtc_dev = rtc_device_register(
pdev->name, &pdev->dev, &tegra_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) {
ret = PTR_ERR(info->rtc_dev);
info->rtc_dev = NULL;
dev_err(&pdev->dev,
"Unable to register device (err=%d).\n",
ret);
goto err_iounmap;
}
ret = request_irq(info->tegra_rtc_irq, tegra_rtc_irq_handler,
IRQF_TRIGGER_HIGH, "rtc alarm", &pdev->dev);
if (ret) {
dev_err(&pdev->dev,
"Unable to request interrupt for device (err=%d).\n",
ret);
goto err_dev_unreg;
}
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
return 0;
err_dev_unreg:
rtc_device_unregister(info->rtc_dev);
err_iounmap:
iounmap(info->rtc_base);
err_release_mem_region:
release_mem_region(res->start, resource_size(res));
err_free_info:
kfree(info);
return ret;
}
static int __devexit tegra_rtc_remove(struct platform_device *pdev)
{
struct tegra_rtc_info *info = platform_get_drvdata(pdev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EBUSY;
free_irq(info->tegra_rtc_irq, &pdev->dev);
rtc_device_unregister(info->rtc_dev);
iounmap(info->rtc_base);
release_mem_region(res->start, resource_size(res));
kfree(info);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
static int tegra_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct device *dev = &pdev->dev;
struct tegra_rtc_info *info = platform_get_drvdata(pdev);
tegra_rtc_wait_while_busy(dev);
/* only use ALARM0 as a wake source. */
writel(0xffffffff, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
writel(TEGRA_RTC_INTR_STATUS_SEC_ALARM0,
info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
dev_vdbg(dev, "alarm sec = %d\n",
readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0));
dev_vdbg(dev, "Suspend (device_may_wakeup=%d) irq:%d\n",
device_may_wakeup(dev), info->tegra_rtc_irq);
/* leave the alarms on as a wake source. */
if (device_may_wakeup(dev))
enable_irq_wake(info->tegra_rtc_irq);
return 0;
}
static int tegra_rtc_resume(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra_rtc_info *info = platform_get_drvdata(pdev);
dev_vdbg(dev, "Resume (device_may_wakeup=%d)\n",
device_may_wakeup(dev));
/* alarms were left on as a wake source, turn them off. */
if (device_may_wakeup(dev))
disable_irq_wake(info->tegra_rtc_irq);
return 0;
}
#endif
static void tegra_rtc_shutdown(struct platform_device *pdev)
{
dev_vdbg(&pdev->dev, "disabling interrupts.\n");
tegra_rtc_alarm_irq_enable(&pdev->dev, 0);
}
MODULE_ALIAS("platform:tegra_rtc");
static struct platform_driver tegra_rtc_driver = {
.remove = __devexit_p(tegra_rtc_remove),
.shutdown = tegra_rtc_shutdown,
.driver = {
.name = "tegra_rtc",
.owner = THIS_MODULE,
},
#ifdef CONFIG_PM
.suspend = tegra_rtc_suspend,
.resume = tegra_rtc_resume,
#endif
};
static int __init tegra_rtc_init(void)
{
return platform_driver_probe(&tegra_rtc_driver, tegra_rtc_probe);
}
module_init(tegra_rtc_init);
static void __exit tegra_rtc_exit(void)
{
platform_driver_unregister(&tegra_rtc_driver);
}
module_exit(tegra_rtc_exit);
MODULE_AUTHOR("Jon Mayo <jmayo@nvidia.com>");
MODULE_DESCRIPTION("driver for Tegra internal RTC");
MODULE_LICENSE("GPL");

View File

@@ -597,6 +597,7 @@ static int scsi_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
break;
default:
ret = BLKPREP_KILL;
goto out;
}

View File

@@ -574,6 +574,7 @@ static const struct backlight_ops dcon_bl_ops = {
static struct backlight_properties dcon_bl_props = {
.max_brightness = 15,
.type = BACKLIGHT_RAW,
.power = FB_BLANK_UNBLANK,
};

View File

@@ -781,6 +781,7 @@ static int __init samsung_init(void)
/* create a backlight device to talk to this one */
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = sabi_config->max_brightness;
backlight_device = backlight_device_register("samsung", &sdev->dev,
NULL, &backlight_ops,

View File

@@ -322,7 +322,7 @@ void tty_schedule_flip(struct tty_struct *tty)
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
schedule_delayed_work(&tty->buf.work, 1);
schedule_work(&tty->buf.work);
}
EXPORT_SYMBOL(tty_schedule_flip);
@@ -402,7 +402,7 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work.work);
container_of(work, struct tty_struct, buf.work);
unsigned long flags;
struct tty_ldisc *disc;
@@ -443,7 +443,7 @@ static void flush_to_ldisc(struct work_struct *work)
if (test_bit(TTY_FLUSHPENDING, &tty->flags))
break;
if (!tty->receive_room || seen_tail) {
schedule_delayed_work(&tty->buf.work, 1);
schedule_work(&tty->buf.work);
break;
}
if (count > tty->receive_room)
@@ -481,7 +481,7 @@ static void flush_to_ldisc(struct work_struct *work)
*/
void tty_flush_to_ldisc(struct tty_struct *tty)
{
flush_delayed_work(&tty->buf.work);
flush_work(&tty->buf.work);
}
/**
@@ -506,9 +506,9 @@ void tty_flip_buffer_push(struct tty_struct *tty)
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
flush_to_ldisc(&tty->buf.work);
else
schedule_delayed_work(&tty->buf.work, 1);
schedule_work(&tty->buf.work);
}
EXPORT_SYMBOL(tty_flip_buffer_push);
@@ -529,6 +529,6 @@ void tty_buffer_init(struct tty_struct *tty)
tty->buf.tail = NULL;
tty->buf.free = NULL;
tty->buf.memory_used = 0;
INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
INIT_WORK(&tty->buf.work, flush_to_ldisc);
}

View File

@@ -529,7 +529,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
static int tty_ldisc_halt(struct tty_struct *tty)
{
clear_bit(TTY_LDISC, &tty->flags);
return cancel_delayed_work_sync(&tty->buf.work);
return cancel_work_sync(&tty->buf.work);
}
/**
@@ -542,7 +542,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty)
{
flush_work_sync(&tty->hangup_work);
flush_work_sync(&tty->SAK_work);
flush_delayed_work_sync(&tty->buf.work);
flush_work_sync(&tty->buf.work);
}
/**
@@ -722,9 +722,9 @@ enable:
/* Restart the work queue in case no characters kick it off. Safe if
already running */
if (work)
schedule_delayed_work(&tty->buf.work, 1);
schedule_work(&tty->buf.work);
if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1);
schedule_work(&o_tty->buf.work);
mutex_unlock(&tty->ldisc_mutex);
tty_unlock();
return retval;
@@ -830,12 +830,12 @@ void tty_ldisc_hangup(struct tty_struct *tty)
/*
* this is like tty_ldisc_halt, but we need to give up
* the BTM before calling cancel_delayed_work_sync,
* which may need to wait for another function taking the BTM
* the BTM before calling cancel_work_sync, which may
* need to wait for another function taking the BTM
*/
clear_bit(TTY_LDISC, &tty->flags);
tty_unlock();
cancel_delayed_work_sync(&tty->buf.work);
cancel_work_sync(&tty->buf.work);
mutex_unlock(&tty->ldisc_mutex);
tty_lock();

View File

@@ -282,6 +282,7 @@ static int appledisplay_probe(struct usb_interface *iface,
snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
atomic_inc_return(&count_displays) - 1);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
pdata->bd = backlight_device_register(bl_name, NULL, pdata,
&appledisplay_bl_data, &props);

View File

@@ -60,6 +60,7 @@ static int move_iovec_hdr(struct iovec *from, struct iovec *to,
{
int seg = 0;
size_t size;
while (len && seg < iov_count) {
size = min(from->iov_len, len);
to->iov_base = from->iov_base;
@@ -79,6 +80,7 @@ static void copy_iovec_hdr(const struct iovec *from, struct iovec *to,
{
int seg = 0;
size_t size;
while (len && seg < iovcount) {
size = min(from->iov_len, len);
to->iov_base = from->iov_base;
@@ -211,12 +213,13 @@ static int peek_head_len(struct sock *sk)
{
struct sk_buff *head;
int len = 0;
unsigned long flags;
lock_sock(sk);
spin_lock_irqsave(&sk->sk_receive_queue.lock, flags);
head = skb_peek(&sk->sk_receive_queue);
if (head)
if (likely(head))
len = head->len;
release_sock(sk);
spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags);
return len;
}
@@ -227,6 +230,7 @@ static int peek_head_len(struct sock *sk)
* @iovcount - returned count of io vectors we fill
* @log - vhost log
* @log_num - log offset
* @quota - headcount quota, 1 for big buffer
* returns number of buffer heads allocated, negative on error
*/
static int get_rx_bufs(struct vhost_virtqueue *vq,
@@ -234,7 +238,8 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
int datalen,
unsigned *iovcount,
struct vhost_log *log,
unsigned *log_num)
unsigned *log_num,
unsigned int quota)
{
unsigned int out, in;
int seg = 0;
@@ -242,7 +247,7 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
unsigned d;
int r, nlogs = 0;
while (datalen > 0) {
while (datalen > 0 && headcount < quota) {
if (unlikely(seg >= UIO_MAXIOV)) {
r = -ENOBUFS;
goto err;
@@ -282,117 +287,7 @@ err:
/* Expects to be always run from workqueue - which acts as
* read-size critical section for our kind of RCU. */
static void handle_rx_big(struct vhost_net *net)
{
struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
unsigned out, in, log, s;
int head;
struct vhost_log *vq_log;
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_control = NULL, /* FIXME: get and handle RX aux data. */
.msg_controllen = 0,
.msg_iov = vq->iov,
.msg_flags = MSG_DONTWAIT,
};
struct virtio_net_hdr hdr = {
.flags = 0,
.gso_type = VIRTIO_NET_HDR_GSO_NONE
};
size_t len, total_len = 0;
int err;
size_t hdr_size;
/* TODO: check that we are running from vhost_worker? */
struct socket *sock = rcu_dereference_check(vq->private_data, 1);
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
return;
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);
hdr_size = vq->vhost_hlen;
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
vq->log : NULL;
for (;;) {
head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
ARRAY_SIZE(vq->iov),
&out, &in,
vq_log, &log);
/* On error, stop handling until the next kick. */
if (unlikely(head < 0))
break;
/* OK, now we need to know about added descriptors. */
if (head == vq->num) {
if (unlikely(vhost_enable_notify(vq))) {
/* They have slipped one in as we were
* doing that: check again. */
vhost_disable_notify(vq);
continue;
}
/* Nothing new? Wait for eventfd to tell us
* they refilled. */
break;
}
/* We don't need to be notified again. */
if (out) {
vq_err(vq, "Unexpected descriptor format for RX: "
"out %d, int %d\n",
out, in);
break;
}
/* Skip header. TODO: support TSO/mergeable rx buffers. */
s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, in);
msg.msg_iovlen = in;
len = iov_length(vq->iov, in);
/* Sanity check */
if (!len) {
vq_err(vq, "Unexpected header len for RX: "
"%zd expected %zd\n",
iov_length(vq->hdr, s), hdr_size);
break;
}
err = sock->ops->recvmsg(NULL, sock, &msg,
len, MSG_DONTWAIT | MSG_TRUNC);
/* TODO: Check specific error and bomb out unless EAGAIN? */
if (err < 0) {
vhost_discard_vq_desc(vq, 1);
break;
}
/* TODO: Should check and handle checksum. */
if (err > len) {
pr_debug("Discarded truncated rx packet: "
" len %d > %zd\n", err, len);
vhost_discard_vq_desc(vq, 1);
continue;
}
len = err;
err = memcpy_toiovec(vq->hdr, (unsigned char *)&hdr, hdr_size);
if (err) {
vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
vq->iov->iov_base, err);
break;
}
len += hdr_size;
vhost_add_used_and_signal(&net->dev, vq, head, len);
if (unlikely(vq_log))
vhost_log_write(vq, vq_log, log, len);
total_len += len;
if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
vhost_poll_queue(&vq->poll);
break;
}
}
mutex_unlock(&vq->mutex);
}
/* Expects to be always run from workqueue - which acts as
* read-size critical section for our kind of RCU. */
static void handle_rx_mergeable(struct vhost_net *net)
static void handle_rx(struct vhost_net *net)
{
struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
unsigned uninitialized_var(in), log;
@@ -405,19 +300,18 @@ static void handle_rx_mergeable(struct vhost_net *net)
.msg_iov = vq->iov,
.msg_flags = MSG_DONTWAIT,
};
struct virtio_net_hdr_mrg_rxbuf hdr = {
.hdr.flags = 0,
.hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE
};
size_t total_len = 0;
int err, headcount;
int err, headcount, mergeable;
size_t vhost_hlen, sock_hlen;
size_t vhost_len, sock_len;
/* TODO: check that we are running from vhost_worker? */
struct socket *sock = rcu_dereference_check(vq->private_data, 1);
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
if (!sock)
return;
mutex_lock(&vq->mutex);
@@ -427,12 +321,14 @@ static void handle_rx_mergeable(struct vhost_net *net)
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
vq->log : NULL;
mergeable = vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF);
while ((sock_len = peek_head_len(sock->sk))) {
sock_len += sock_hlen;
vhost_len = sock_len + vhost_hlen;
headcount = get_rx_bufs(vq, vq->heads, vhost_len,
&in, vq_log, &log);
&in, vq_log, &log,
likely(mergeable) ? UIO_MAXIOV : 1);
/* On error, stop handling until the next kick. */
if (unlikely(headcount < 0))
break;
@@ -476,7 +372,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
break;
}
/* TODO: Should check and handle checksum. */
if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF) &&
if (likely(mergeable) &&
memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,
offsetof(typeof(hdr), num_buffers),
sizeof hdr.num_buffers)) {
@@ -498,14 +394,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
mutex_unlock(&vq->mutex);
}
static void handle_rx(struct vhost_net *net)
{
if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF))
handle_rx_mergeable(net);
else
handle_rx_big(net);
}
static void handle_tx_kick(struct vhost_work *work)
{
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
@@ -654,6 +542,7 @@ static struct socket *get_raw_socket(int fd)
} uaddr;
int uaddr_len = sizeof uaddr, r;
struct socket *sock = sockfd_lookup(fd, &r);
if (!sock)
return ERR_PTR(-ENOTSOCK);
@@ -682,6 +571,7 @@ static struct socket *get_tap_socket(int fd)
{
struct file *file = fget(fd);
struct socket *sock;
if (!file)
return ERR_PTR(-EBADF);
sock = tun_get_socket(file);
@@ -696,6 +586,7 @@ static struct socket *get_tap_socket(int fd)
static struct socket *get_socket(int fd)
{
struct socket *sock;
/* special case to disable backend */
if (fd == -1)
return NULL;
@@ -741,9 +632,9 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
oldsock = rcu_dereference_protected(vq->private_data,
lockdep_is_held(&vq->mutex));
if (sock != oldsock) {
vhost_net_disable_vq(n, vq);
rcu_assign_pointer(vq->private_data, sock);
vhost_net_enable_vq(n, vq);
vhost_net_disable_vq(n, vq);
rcu_assign_pointer(vq->private_data, sock);
vhost_net_enable_vq(n, vq);
}
mutex_unlock(&vq->mutex);
@@ -768,6 +659,7 @@ static long vhost_net_reset_owner(struct vhost_net *n)
struct socket *tx_sock = NULL;
struct socket *rx_sock = NULL;
long err;
mutex_lock(&n->dev.mutex);
err = vhost_dev_check_owner(&n->dev);
if (err)
@@ -829,6 +721,7 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
struct vhost_vring_file backend;
u64 features;
int r;
switch (ioctl) {
case VHOST_NET_SET_BACKEND:
if (copy_from_user(&backend, argp, sizeof backend))

View File

@@ -41,8 +41,8 @@ static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh,
poll_table *pt)
{
struct vhost_poll *poll;
poll = container_of(pt, struct vhost_poll, table);
poll = container_of(pt, struct vhost_poll, table);
poll->wqh = wqh;
add_wait_queue(wqh, &poll->wait);
}
@@ -85,6 +85,7 @@ void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,
void vhost_poll_start(struct vhost_poll *poll, struct file *file)
{
unsigned long mask;
mask = file->f_op->poll(file, &poll->table);
if (mask)
vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
@@ -101,6 +102,7 @@ static bool vhost_work_seq_done(struct vhost_dev *dev, struct vhost_work *work,
unsigned seq)
{
int left;
spin_lock_irq(&dev->work_lock);
left = seq - work->done_seq;
spin_unlock_irq(&dev->work_lock);
@@ -222,6 +224,7 @@ static int vhost_worker(void *data)
static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
{
int i;
for (i = 0; i < dev->nvqs; ++i) {
dev->vqs[i].indirect = kmalloc(sizeof *dev->vqs[i].indirect *
UIO_MAXIOV, GFP_KERNEL);
@@ -235,6 +238,7 @@ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
goto err_nomem;
}
return 0;
err_nomem:
for (; i >= 0; --i) {
kfree(dev->vqs[i].indirect);
@@ -247,6 +251,7 @@ err_nomem:
static void vhost_dev_free_iovecs(struct vhost_dev *dev)
{
int i;
for (i = 0; i < dev->nvqs; ++i) {
kfree(dev->vqs[i].indirect);
dev->vqs[i].indirect = NULL;
@@ -296,26 +301,28 @@ long vhost_dev_check_owner(struct vhost_dev *dev)
}
struct vhost_attach_cgroups_struct {
struct vhost_work work;
struct task_struct *owner;
int ret;
struct vhost_work work;
struct task_struct *owner;
int ret;
};
static void vhost_attach_cgroups_work(struct vhost_work *work)
{
struct vhost_attach_cgroups_struct *s;
s = container_of(work, struct vhost_attach_cgroups_struct, work);
s->ret = cgroup_attach_task_all(s->owner, current);
struct vhost_attach_cgroups_struct *s;
s = container_of(work, struct vhost_attach_cgroups_struct, work);
s->ret = cgroup_attach_task_all(s->owner, current);
}
static int vhost_attach_cgroups(struct vhost_dev *dev)
{
struct vhost_attach_cgroups_struct attach;
attach.owner = current;
vhost_work_init(&attach.work, vhost_attach_cgroups_work);
vhost_work_queue(dev, &attach.work);
vhost_work_flush(dev, &attach.work);
return attach.ret;
struct vhost_attach_cgroups_struct attach;
attach.owner = current;
vhost_work_init(&attach.work, vhost_attach_cgroups_work);
vhost_work_queue(dev, &attach.work);
vhost_work_flush(dev, &attach.work);
return attach.ret;
}
/* Caller should have device mutex */
@@ -323,11 +330,13 @@ static long vhost_dev_set_owner(struct vhost_dev *dev)
{
struct task_struct *worker;
int err;
/* Is there an owner already? */
if (dev->mm) {
err = -EBUSY;
goto err_mm;
}
/* No owner, become one */
dev->mm = get_task_mm(current);
worker = kthread_create(vhost_worker, dev, "vhost-%d", current->pid);
@@ -380,6 +389,7 @@ long vhost_dev_reset_owner(struct vhost_dev *dev)
void vhost_dev_cleanup(struct vhost_dev *dev)
{
int i;
for (i = 0; i < dev->nvqs; ++i) {
if (dev->vqs[i].kick && dev->vqs[i].handle_kick) {
vhost_poll_stop(&dev->vqs[i].poll);
@@ -421,6 +431,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
{
u64 a = addr / VHOST_PAGE_SIZE / 8;
/* Make sure 64 bit math will not overflow. */
if (a > ULONG_MAX - (unsigned long)log_base ||
a + (unsigned long)log_base > ULONG_MAX)
@@ -461,6 +472,7 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_memory *mem,
int log_all)
{
int i;
for (i = 0; i < d->nvqs; ++i) {
int ok;
mutex_lock(&d->vqs[i].mutex);
@@ -527,6 +539,7 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
{
struct vhost_memory mem, *newmem, *oldmem;
unsigned long size = offsetof(struct vhost_memory, regions);
if (copy_from_user(&mem, m, size))
return -EFAULT;
if (mem.padding)
@@ -544,7 +557,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
return -EFAULT;
}
if (!memory_access_ok(d, newmem, vhost_has_feature(d, VHOST_F_LOG_ALL))) {
if (!memory_access_ok(d, newmem,
vhost_has_feature(d, VHOST_F_LOG_ALL))) {
kfree(newmem);
return -EFAULT;
}
@@ -560,6 +574,7 @@ static int init_used(struct vhost_virtqueue *vq,
struct vring_used __user *used)
{
int r = put_user(vq->used_flags, &used->flags);
if (r)
return r;
return get_user(vq->last_used_idx, &used->idx);
@@ -849,6 +864,7 @@ static const struct vhost_memory_region *find_region(struct vhost_memory *mem,
{
struct vhost_memory_region *reg;
int i;
/* linear search is not brilliant, but we really have on the order of 6
* regions in practice */
for (i = 0; i < mem->nregions; ++i) {
@@ -871,6 +887,7 @@ static int set_bit_to_user(int nr, void __user *addr)
void *base;
int bit = nr + (log % PAGE_SIZE) * 8;
int r;
r = get_user_pages_fast(log, 1, 1, &page);
if (r < 0)
return r;
@@ -888,6 +905,7 @@ static int log_write(void __user *log_base,
{
u64 write_page = write_address / VHOST_PAGE_SIZE;
int r;
if (!write_length)
return 0;
write_length += write_address % VHOST_PAGE_SIZE;
@@ -1037,8 +1055,8 @@ static int get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
i, count);
return -EINVAL;
}
if (unlikely(memcpy_fromiovec((unsigned char *)&desc, vq->indirect,
sizeof desc))) {
if (unlikely(memcpy_fromiovec((unsigned char *)&desc,
vq->indirect, sizeof desc))) {
vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n",
i, (size_t)indirect->addr + i * sizeof desc);
return -EINVAL;
@@ -1153,7 +1171,7 @@ int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
i, vq->num, head);
return -EINVAL;
}
ret = copy_from_user(&desc, vq->desc + i, sizeof desc);
ret = __copy_from_user(&desc, vq->desc + i, sizeof desc);
if (unlikely(ret)) {
vq_err(vq, "Failed to get descriptor: idx %d addr %p\n",
i, vq->desc + i);
@@ -1317,6 +1335,7 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq)
{
__u16 flags;
/* Flush out used index updates. This is paired
* with the barrier that the Guest executes when enabling
* interrupts. */
@@ -1361,6 +1380,7 @@ bool vhost_enable_notify(struct vhost_virtqueue *vq)
{
u16 avail_idx;
int r;
if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY))
return false;
vq->used_flags &= ~VRING_USED_F_NO_NOTIFY;
@@ -1387,6 +1407,7 @@ bool vhost_enable_notify(struct vhost_virtqueue *vq)
void vhost_disable_notify(struct vhost_virtqueue *vq)
{
int r;
if (vq->used_flags & VRING_USED_F_NO_NOTIFY)
return;
vq->used_flags |= VRING_USED_F_NO_NOTIFY;

View File

@@ -127,6 +127,7 @@ static void init_backlight(struct atmel_lcdfb_info *sinfo)
return;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
bl = backlight_device_register("backlight", &sinfo->pdev->dev, sinfo,
&atmel_lcdc_bl_ops, &props);

View File

@@ -1818,6 +1818,7 @@ static void aty128_bl_init(struct aty128fb_par *par)
snprintf(name, sizeof(name), "aty128bl%d", info->node);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
bd = backlight_device_register(name, info->dev, par, &aty128_bl_data,
&props);

View File

@@ -2241,6 +2241,7 @@ static void aty_bl_init(struct atyfb_par *par)
snprintf(name, sizeof(name), "atybl%d", info->node);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
bd = backlight_device_register(name, info->dev, par, &aty_bl_data,
&props);

View File

@@ -158,6 +158,7 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo)
snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
bd = backlight_device_register(name, rinfo->info->dev, pdata,
&radeon_bl_data, &props);

View File

@@ -227,6 +227,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
}
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = MAX_BRIGHTNESS;
bl = backlight_device_register(name, &pdev->dev, data,
&pm860x_backlight_ops, &props);

Some files were not shown because too many files have changed in this diff Show More