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:
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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++;
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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))
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
724
drivers/dma/mxs-dma.c
Normal 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);
|
@@ -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
@@ -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;
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
@@ -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
115
drivers/firmware/sigma.c
Normal 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);
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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)
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
535
drivers/i2c/busses/i2c-diolan-u2c.c
Normal file
535
drivers/i2c/busses/i2c-diolan-u2c.c
Normal 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");
|
@@ -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));
|
||||
|
@@ -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));
|
||||
|
176
drivers/i2c/busses/i2c-pxa-pci.c
Normal file
176
drivers/i2c/busses/i2c-pxa-pci.c
Normal 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>");
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
378
drivers/leds/leds-lm3530.c
Normal 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>");
|
@@ -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[] = {
|
||||
|
@@ -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[] = {
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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[] = {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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",
|
||||
|
908
drivers/misc/spear13xx_pcie_gadget.c
Normal file
908
drivers/misc/spear13xx_pcie_gadget.c
Normal 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");
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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)) {
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
};
|
||||
|
@@ -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,
|
||||
|
@@ -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
488
drivers/rtc/rtc-tegra.c
Normal 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");
|
@@ -597,6 +597,7 @@ static int scsi_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = BLKPREP_KILL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
};
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
|
@@ -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))
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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
Reference in New Issue
Block a user